library(tidyverse)
library(ggplot2)
library(ggalluvial)
library(biomaRt)
library(ggdendro)
library(pcaMethods)
library(uwot)
library(pheatmap)
library(ggplotify)
library (ggrepel)
library(ggraph)
library(patchwork)
select <- dplyr::select
tmm_sample <- read_csv("./data/final_data/curated_pTMM_rattus_norvegicus_v103.csv")
Rows: 22245 Columns: 353── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): target_id
dbl (352): ventral.forebrain_male.3, ventral.forebrain_male.2, ventral.forebrain_female.3, ventral.forebrain_female...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
metadata <- read_csv("./data/final_data/curated_metadata.csv")
Rows: 352 Columns: 13── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (11): ID, Acronym, sex, tissue_name, region_tissue_name, consensus_tissue_name, organ_name, tissue_color, regio...
dbl (1): rat_n
lgl (1): regional_tissue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
all(sort(colnames(tmm_sample[-1])) == sort(metadata$ID))
[1] TRUE
#Generate hierarchical data
#slow, but understanable
tmm_tissue <- tmm_sample %>%
gather(sample, tmm, -1) %>%
left_join(metadata %>% select(ID,tissue_name), by = c("sample" = "ID")) %>%
group_by(target_id, tissue_name) %>%
summarize(tmm = mean(tmm)) %>%
ungroup()
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
write_csv(tmm_tissue, file="./data/final_data/final_tmm_tissue_name.csv")
tmm_region_tissue <- tmm_tissue %>%
left_join(metadata %>% select(tissue_name,region_tissue_name), by = c("tissue_name" = "tissue_name")) %>%
group_by(target_id, region_tissue_name) %>%
summarize(tmm = max(tmm)) %>%
ungroup()
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
write_csv(tmm_region_tissue, file="./data/final_data/final_tmm_region_tissue_name.csv")
tmm_consensus_tissue <- tmm_region_tissue %>%
left_join(metadata %>% select(region_tissue_name,consensus_tissue_name), by = c("region_tissue_name" = "region_tissue_name")) %>%
group_by(target_id, consensus_tissue_name) %>%
summarize(tmm = max(tmm)) %>%
ungroup()
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
write_csv(tmm_consensus_tissue, file="./data/final_data/final_tmm_consensus_name.csv")
#check
all(metadata$tissue_name %>% unique() %>% sort() == tmm_tissue$tissue_name %>% unique() %>% sort())
[1] TRUE
all(metadata$region_tissue_name %>% unique() %>% sort() == tmm_region_tissue$region_tissue_name %>% unique() %>% sort())
[1] TRUE
all(metadata$consensus_tissue_name %>% unique() %>% sort() == tmm_consensus_tissue$consensus_tissue_name %>% unique() %>% sort())
[1] TRUE
# # faster, but less understandable
# unique_tissue <- select(metadata,tissue_name) %>%
# distinct()
# tmm_by_tissue <- tibble(target_id = tmm_sample$target_id)
# for (i in unique_tissue$tissue_name) {
# curent_tissue_meta = metadata[metadata$tissue_name == i,]
# current_tmm = select(tmm_sample,c(target_id, curent_tissue_meta$ID))
# means <- current_tmm %>% select(curent_tissue_meta$ID) %>% rowMeans()
# tmm_by_tissue[toString(i)] <- means
# }
# #tmm_by_tissue <- tmm_by_tissue %>% gather(tissue_name, tmm, -1)
# #write.csv(tmm_by_tissue, file="tmm_tissue_name.csv", row.names = FALSE)
#
# unique_region <- select(metadata,region_tissue_name) %>%
# distinct()
# tmm_by_region <- tibble(target_id = tmm_sample$target_id)
# for (i in unique_region$region_tissue_name) {
# current_region_meta = metadata[metadata$region_tissue_name == i,]
# current_tmm = select(tmm_by_tissue, c(target_id, unique(current_region_meta$tissue_name)))
# max <- select(current_tmm,-target_id) %>% apply(1,max)
# tmm_by_region[toString(i)] <- max
# }
#
# # write.csv(tmm_by_region, file="tmm_region_name.csv", row.names = FALSE)
#
# unique_consensus <- select(metadata,consensus_tissue_name) %>%
# distinct()
# tmm_by_consensus <- tibble(target_id = tmm_sample$target_id)
# for (i in unique_consensus$consensus_tissue_name) {
# current_consensus_meta = metadata[metadata$consensus_tissue_name == i,]
# current_tmm = select(tmm_by_region, c(target_id, unique(current_consensus_meta$region_tissue_name)))
# max <- select(current_tmm,-target_id) %>% apply(1,max)
# tm_by_consensus[toString(i)] <- max
# }
#write.csv(tmm_by_consensus, file="tmm_consensus_name.csv", row.names = FALSE)
#Figure 1 ##Figure 1A - Hierarchy Overview
tissue_colors_palette_full <- rbind(
metadata %>% select(name = tissue_name, color = tissue_color),
metadata %>% select(name = consensus_tissue_name, color = consensus_tissue_color),
metadata %>% select(name = organ_name, color = organ_color)
) %>% distinct() %>%
mutate(name = str_to_sentence(name)) %>% arrange(name)
pal <- tissue_colors_palette_full$color
pal <- set_names(pal,tissue_colors_palette_full$name )
plot_data1 <-
metadata %>%
select(tissue_name, region_tissue_name, consensus_tissue_name, organ_name) %>% #,
#tissue_color, region_tissue_color, consensus_tissue_color, organ_color) %>%
mutate(tissue_name = str_to_sentence(tissue_name), region_tissue_name = str_to_sentence(region_tissue_name), consensus_tissue_name = str_to_sentence(consensus_tissue_name), organ_name = str_to_sentence( organ_name)) %>% unique() %>%
mutate(organ_name = factor(case_when(organ_name == "Male reproductive system" ~
"Male tissues",
organ_name == "Breast and female reproductive system" ~
"Female tissues",
organ_name == "Adipose & soft tissue" ~
"Connective & soft tissue",
organ_name == "Bone marrow & immune system" ~
"Bone marrow & lymphoid tissues",
T ~ organ_name),
c("Brain",
"Eye",
"Endocrine tissues",
"Respiratory system",
"Proximal digestive tract",
"Gastrointestinal tract",
"Liver & gallbladder",
"Kidney & urinary bladder",
"Pancreas",
"Male tissues",
"Female tissues",
"Muscle tissues",
"Connective & soft tissue",
"Skin",
"Bone marrow & lymphoid tissues")))
plot_data1 <- plot_data1 %>%
arrange(organ_name,
consensus_tissue_name,
region_tissue_name,
tissue_name) %>%
mutate(plot_order = row_number())
plot_data2 <-
plot_data1 %>%
select(-region_tissue_name) %>%
gather(column, label, -plot_order) %>%
group_by(label, column) %>%
summarise(plot_order = mean(plot_order)) %>%
ungroup() %>%
mutate(label = label,
column = factor(column,
c("organ_name",
"consensus_tissue_name",
"tissue_name")))
Warning: attributes are not identical across measure variables;
they will be dropped`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
plot_data3 <-
plot_data1 %>%
group_by(consensus_tissue_name) %>%
mutate(left_pos = mean(plot_order))
plot_data4 <-
plot_data2 %>%
left_join(tissue_colors_palette_full,
by = c("label" = "name")) %>%
group_by(column, label) %>%
summarise(miny = min(plot_order) - 0.5,
maxy = max(plot_order) + 0.5)
`summarise()` has grouped output by 'column'. You can override using the `.groups` argument.
ggplot() +
geom_rect(data = plot_data4,
aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, fill = label),
show.legend = F
) +
geom_rect(data = plot_data4 %>% filter (column == "tissue_name"),
# aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color),
aes(xmin = 3, xmax = 4, ymin = miny, ymax = maxy, fill = label),
show.legend = F
) +
geom_rect(data = plot_data4 %>% filter (column == "consensus_tissue_name"),
# aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color),
aes(xmin = 1, xmax = 2, ymin = miny, ymax = maxy, fill = label),
show.legend = F, width = 10
) +
geom_segment(
data = plot_data3,
aes(
x = "consensus_tissue_name",
xend = "tissue_name",
y = left_pos,
yend = plot_order,
color = tissue_name
),
show.legend = F,
alpha = 0.5,
size = 2
)+
geom_text(
data = plot_data2 %>% filter(column == "consensus_tissue_name"),
aes(x = column, y = plot_order, label = label),
hjust = 1,
size = 2 * 5 / 6,
label.padding = unit(0, "mm")
) +
geom_text(
data = plot_data2 %>% filter(column == "tissue_name"),
aes(x = column, y = plot_order, label = label),
hjust = 0,
size = 2 * 5 / 6,
show.legend = F,
label.padding = unit(0, "mm")
) +
geom_label(
data = plot_data2 %>%
filter(column == "organ_name"),
#aes(x = column, y = plot_order, label = label),
aes(x = column, y = plot_order, label = gsub(" ", "\n", label)),
show.legend = F,
label.size = 0,
hjust = 1,
lineheight = 0.7,
label.padding = unit(0, "mm"),
size = 2 * 5 / 6
) +
scale_y_reverse() +
scale_x_discrete(labels = c('Organ system', "Grouped tissue", "Tissue type"),position = "top") +
scale_fill_manual(values = pal) +
scale_color_manual(values = pal) +
theme_void() +
theme(axis.text.x = element_text())
Warning: Ignoring unknown parameters: `width`Warning: Ignoring unknown parameters: `label.padding`Warning: Ignoring unknown parameters: `label.padding`
ggsave("final_plots/tissues.pdf", height = 7, width = 4)

##Figure 1B - Ward’s Retina-Dendrogramm
##Functino based on Max Karlsson's retinogramm
circular_dendrogram_retinastyle_2 <-
function(clust, color_mapping, label_col, color_col,
scale_expansion = c(0.25, 0.25), text_size = 3, width_range = c(1.5, 6),
arc_strength = 0.8, default_color = "gray80") {
require(ggraph)
require(igraph)
require(viridis)
require(tidyverse)
require(magrittr)
dendrogram <-
clust %>%
as.dendrogram()
g <-
ggraph(dendrogram, layout = 'dendrogram', circular = T)
#
# g +
# geom_edge_fan(data = edge_data %>%
# mutate(hghl = edge.id == 99),
# aes(label = edge_id, color = as.factor(rank_radius)),
# width =4) +
# geom_node_text(aes(label = label))
edge_data <-
get_edges()(g$data) %>%
as_tibble() %>%
left_join(color_mapping %>%
select(label = label_col,
color = color_col),
by = c("node2.label" = "label")) %>%
mutate(radius = xend^2 + yend^2,
edge.id = as.character(edge.id)) %>%
arrange(-radius) %>%
mutate(edge_id = as.character(row_number()),
rank_radius = unclass(factor(-radius)),
x_m = round(x, 10),
y_m = round(y, 10),
xend_m = round(xend, 10),
yend_m = round(yend, 10))
edge_id_colors <-
edge_data %>%
filter(!is.na(color)) %$%
set_names(color, edge_id)
for(rank_rad in 2:max(edge_data$rank_radius)) {
edge_id_colors_new <-
left_join(edge_data %>%
select(edge_id, radius, xend_m, yend_m, rank_radius) %>%
filter(rank_radius == rank_rad),
edge_data %>%
select(edge_id, radius, x_m, y_m, rank_radius) %>%
filter(rank_radius < rank_rad),
by = c("xend_m" = "x_m", "yend_m" = "y_m")) %>%
left_join(enframe(edge_id_colors),
by = c("edge_id.y" = "name")) %>%
group_by(edge_id.x) %>%
summarise(color = ifelse(n_distinct(value) == 1 & any(value != default_color),
as.character(unique(value)),
default_color)) %$%
set_names(color, edge_id.x)
edge_id_colors <-
c(edge_id_colors, edge_id_colors_new)
}
g +
scale_edge_width(range = width_range)+
geom_edge_diagonal(data = edge_data,
aes(edge_color = as.character(edge_id),
edge_width = 1 - sqrt(xend^2 + yend^2)),
strength = arc_strength,
show.legend = F) +
scale_edge_color_manual(values = edge_id_colors) +
g$data %>%
filter(label != "") %>%
mutate(degree = case_when(x >= 0 ~ asin(y) * 180 / pi,
x < 0 ~ 360 - asin(y) * 180 / pi)) %>%
left_join(color_mapping %>%
select(label = label_col,
color = color_col),
by = "label") %>%
{geom_node_text(data = .,
aes(label = label),
angle = .$degree,
hjust = ifelse(.$x < 0,
1,
0),
vjust = 0.5,
size = text_size)} +
scale_x_continuous(expand = expand_scale(scale_expansion)) +
scale_y_continuous(expand = expand_scale(scale_expansion)) +
coord_fixed() +
theme_void()
}
hclust4RNAseq_ward <- function(df, correlation_method = "spearman"){
#wide dataframe as input
#to get correlation between samples, where rows are genes columns are samples
#to get correlation between genes across samples, input df with genes as columns
#can use later for dendogram making: ggdendrogram([hclust4RNAseq_results], rotate = FALSE, size = 10, face = "bold")
similarity <- cor(df, method=correlation_method, use="pairwise.complete.obs")
dissimilarity <- 1 - similarity
hcl <- hclust(as.dist(dissimilarity), "ward.D2")
return (hcl)
}
tissue_dendro_ward <- hclust4RNAseq_ward(tmm_tissue %>% mutate(tissue_name = str_to_sentence(tissue_name)) %>% spread(tissue_name, tmm) %>% column_to_rownames("target_id"))
circular_dendrogram_retinastyle_2(
clust = tissue_dendro_ward,
color_mapping = metadata %>%
select(tissue_name, tissue_color) %>%
mutate(tissue_name = str_to_sentence(tissue_name)),
label_col = "tissue_name",
color_col = "tissue_color",
scale_expansion = c(0.7, 0.7),
text_size = 2.4,
width_range = c(0.5, 4),
arc_strength = 0.4,
default_color = "gray80")
ggsave("./final_plots/data_presentation/retinagram_all_tissue_clust_ward.pdf", width = 6, height = 6)

##Figure 1C - Spearman heatmap (grouped tissue)
##Spearman's roh heatmap at grouped tissue level
if(file.exists("./data/final_data/spearman_corr_consensus_tissues.csv")) {
consensus_tmm_spearman <- read_csv("./data/final_data/spearman_corr_consensus_tissues.csv")
} else {
consensus_tmm_spearman <- tmm_consensus_tissue %>%
spread(consensus_tissue_name, tmm) %>%
column_to_rownames("target_id") %>%
cor(method="spearman", use="pairwise.complete.obs") %>%
as.data.frame() %>%
as_tibble(rownames = "consensus_tissue_name")
write_csv(consensus_tmm_spearman,"./data/final_data/spearman_corr_consensus_tissues.csv")
}
Rows: 53 Columns: 54── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): consensus_tissue_name
dbl (53): adipose tissue, adrenal gland, aorta, bone marrow, brain, breast, cartilage, cervix, choroid plexus, circ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
consensus_tmm_spearman %>%
rename_with(str_to_sentence, -1) %>%
mutate(consensus_tissue_name = str_to_sentence(consensus_tissue_name)) %>%
column_to_rownames("consensus_tissue_name") %>%
pheatmap(
# clustering_method = "ward.D2",
cellheight = 8,
cellwidth = 8,
border_color = NA,
color = viridis::inferno(20, direction = -1),
show_rownames = FALSE,
) %>%
as.ggplot()

ggsave("./final_plots/data_presentation/spearman_corr_consensus_tissue.pdf", height = 10, width = 10)

#Figure 2 ##Figure 2A - Sample level PCA Plot
sample_pca <-
tmm_sample %>%
# gather(sample_name, tmm, -1) %>%
# group_by(target_id) %>%
# mutate(sd = sd(tmm)) %>%
# ungroup() %>%
# filter(sd > 0) %>%
# select(-sd) %>%
# spread(sample_name, tmm) %>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames("target_id") %>%
scale() %>%
t() %>%
pca(nPcs = 8)
#sample_pca@scores
summary(sample_pca)
svd calculated PCA
Importance of component(s):
PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8
R2 0.3135 0.09126 0.08083 0.03926 0.03616 0.03489 0.02711 0.02105
Cumulative R2 0.3135 0.40474 0.48557 0.52483 0.56099 0.59588 0.62299 0.64404
plot_data <- sample_pca %>%
scores() %>%
as_tibble(rownames = "sample_id") %>%
left_join(metadata,
by = c("sample_id" = "ID"))
organ_colors <- metadata %>% select(organ_name, organ_color) %>% unique()
pal <- organ_colors$organ_color
pal <- setNames(pal, organ_colors$organ_name)
plot_data %>%
ggplot(aes(PC1, PC2)) +
geom_point(aes(fill = organ_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
scale_fill_manual(values = pal) +
#geom_text(aes(label = sample_id), vjust = 1,hjust = 0, nudge_y =-1) +
xlab(paste("PC1", sample_pca@R2[1] * 100, "% of the variance")) +
ylab(paste("PC2", sample_pca@R2[2] * 100, "% of the variance")) +
theme_classic() + theme(legend.text = element_text(size = 10),
legend.title =element_blank(),
legend.position = "bottom",
legend.spacing.y = unit(-0.3, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(fill = guide_legend(ncol = 3, byrow = TRUE))

#ggsave("./final_plots/data_presentation/sample_pca_1.pdf", width = 8, height = 8)
# ggsave("./final_plots/data_presentation/sample_pca_1_w_filter.pdf", width = 8, height = 8)
#
# plot_data %>%
# ggplot(aes(PC1, PC2)) +
# geom_point(aes(fill = organ_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
# scale_fill_manual(values = pal) +
# #geom_text(aes(label = sample_id), vjust = 1,hjust = 0, nudge_y =-1) +
# xlab(paste("PC1", sample_pca@R2[1] * 100, "% of the variance")) +
# ylab(paste("PC2", sample_pca@R2[2] * 100, "% of the variance")) +
# theme_classic() + theme(legend.text = element_text(size = 10),
# legend.title =element_blank(),
# legend.position = "bottom",
# legend.spacing.y = unit(-0.3, 'cm'),
# legend.spacing.x = unit(-0.01, 'cm')) +
# guides(fill = guide_legend(ncol = 2, byrow = TRUE))
#
# #ggsave("./final_plots/data_presentation/sample_pca_2.pdf", width = 5, height = 5)
# # ggsave("./final_plots/data_presentation/sample_pca_2_w_filter.pdf", width = 7, height = 7)
###Extra PCA plots not in report
sample_pca <-
tmm_sample %>%
# gather(sample_name, tmm, -1) %>%
# group_by(target_id) %>%
# mutate(sd = sd(tmm)) %>%
# ungroup() %>%
# filter(sd > 0) %>%
# select(-sd) %>%
# spread(sample_name, tmm) %>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames("target_id") %>%
scale() %>%
t() %>%
pca(nPcs = 8)
#sample_pca@scores
summary(sample_pca)
svd calculated PCA
Importance of component(s):
PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8
R2 0.3135 0.09126 0.08083 0.03926 0.03616 0.03489 0.02711 0.02105
Cumulative R2 0.3135 0.40474 0.48557 0.52483 0.56099 0.59588 0.62299 0.64404
plot_data <- sample_pca %>%
scores() %>%
as_tibble(rownames = "sample_id") %>%
left_join(metadata,
by = c("sample_id" = "ID"))
region_colors <- metadata %>% select(region_tissue_name, region_tissue_color, organ_name) %>% unique() %>% filter(organ_name == "Brain") %>% select(-organ_name)
pal <- region_colors$region_tissue_color
pal <- setNames(pal, region_colors$region_tissue_name)
plot_data %>%
ggplot(aes(PC1, PC2)) +
geom_point(aes(fill = region_tissue_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
scale_fill_manual(values = pal, na.value = "#FFFFFF") +
xlim(-75,0) +
ylim(-10,5) +
xlab(paste("PC1", sample_pca@R2[1] * 100, "% of the variance")) +
ylab(paste("PC2", sample_pca@R2[2] * 100, "% of the variance")) +
theme_classic() + theme(legend.text = element_text(size = 10),
legend.title =element_blank(),
legend.position = "bottom",
legend.spacing.y = unit(-0.3, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(fill = guide_legend(ncol = 2, byrow = TRUE))
ggsave("./final_plots/data_presentation/brain_w_all_sample_pca.pdf", width = 5, height = 5)

NA
NA
brain_pca <-
tmm_sample %>% select(c(target_id, metadata %>% filter(organ_name =="Brain") %>% .$ID))%>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames("target_id") %>%
scale() %>%
t() %>%
pca(nPcs = 8)
#brain_pca@scores
plot_data <- brain_pca %>%
scores() %>%
as_tibble(rownames = "sample_id") %>%
left_join(metadata,
by = c("sample_id" = "ID"))
region_colors <- metadata %>% select(region_tissue_name, region_tissue_color) %>% unique()
pal <- region_colors$region_tissue_color
pal <- setNames(pal, region_colors$region_tissue_name)
plot_data %>%
ggplot(aes(PC1, PC2)) +
geom_point(aes(fill = region_tissue_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
scale_fill_manual(values = pal) +
#geom_text(aes(label = sample_id), vjust = 1,hjust = 0, nudge_y =-1) +
xlab(paste("PC1", brain_pca@R2[1] * 100, "% of the variance")) +
ylab(paste("PC2", brain_pca@R2[2] * 100, "% of the variance")) +
theme_classic() + theme(legend.text = element_text(size = 10),
legend.title =element_blank(),
legend.position = "bottom",
legend.spacing.y = unit(-0.3, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(fill = guide_legend(ncol = 2, byrow = TRUE))
ggsave("./final_plots/data_presentation/brain_sample_pca.pdf", width = 5, height = 5)

NA
NA
NA
##Figure 2B - UMAP Plot
wide_data = tmm_sample
seed = 4
n_epochs = 1000
n_neighbors = 15
set.seed(seed)
pca_res <-
wide_data %>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames(colnames(wide_data)[1]) %>%
scale() %>%
t() %>%
pca(nPcs = dim(.)[1])
pc_lim <-
which(pca_res@R2cum > 0.8)[1]
pc_lim_sd <-
rev(which(pca_res@sDev > 1))[1]
set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
umap(n_neighbors = n_neighbors,
n_epochs = n_epochs) %>%
#metric = "correlation") %>%
as_tibble() %>%
set_names(paste0("UMAP", 1:ncol(.))) %>%
mutate(sample = rownames(pca_res@scores)) %>%
select(sample, everything()) %>%
left_join(metadata, by = c("sample" = "ID"))
organ_colors <- metadata %>% select(organ_name, organ_color) %>% unique()
pal <- organ_colors$organ_color
pal <- setNames(pal, organ_colors$organ_name)
# umap_res %>% ggplot(aes(UMAP1, UMAP2, color = organ_name)) +
# geom_point(alpha = 0.8) + scale_color_manual(values = pal)
umap_res %>% ggplot(aes(UMAP1, UMAP2)) +
geom_point(
aes(fill = organ_name),
color = "gray25",
alpha = 0.7,
shape = 21,
size = 2,
stroke = 0.5
) + scale_fill_manual(values = pal) +
# theme_bw() + theme(
# panel.border = element_blank(),
# panel.grid.major = element_blank(),
# panel.grid.minor = element_blank(),
# axis.line = element_line(colour = "black"),
# legend.title = element_blank()
# )
theme_classic() + theme(legend.text = element_text(size = 10),
legend.title =element_blank(),
legend.position = "bottom",
legend.spacing.y = unit(-0.3, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(fill = guide_legend(ncol = 2, byrow = TRUE))
ggsave("./final_plots/data_presentation/sample_umap_euclidean.pdf", width = 5, height = 5.5)

##Figure 2C - Brain Umap Plot
#Plot focusing only on brain samples, but UMAP was based on the whole sample set, not only brian samples.
wide_data = tmm_sample
seed = 4
n_epochs = 1000
n_neighbors = 15
set.seed(seed)
pca_res <-
wide_data %>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames(colnames(wide_data)[1]) %>%
scale() %>%
t() %>%
pca(nPcs = dim(.)[1])
pc_lim <-
which(pca_res@R2cum > 0.8)[1]
pc_lim_sd <-
rev(which(pca_res@sDev > 1))[1]
set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
umap(n_neighbors = n_neighbors,
n_epochs = n_epochs) %>%
#metric = "correlation") %>%
as_tibble() %>%
set_names(paste0("UMAP", 1:ncol(.))) %>%
mutate(sample = rownames(pca_res@scores)) %>%
select(sample, everything()) %>%
left_join(metadata, by = c("sample" = "ID"))
region_colors <- metadata %>% select(region_tissue_name, region_tissue_color, organ_name) %>% unique() %>% filter(organ_name == "Brain") %>% select(-organ_name)
pal <- region_colors$region_tissue_color
pal <- setNames(pal, region_colors$region_tissue_name)
# umap_res %>% ggplot(aes(UMAP1, UMAP2, color = organ_name)) +
# geom_point(alpha = 0.8) + scale_color_manual(values = pal)
umap_res %>% ggplot(aes(UMAP1, UMAP2)) +
geom_point(
aes(fill = region_tissue_name),
color = "gray25",
alpha = 0.7,
shape = 21,
size = 2,
stroke = 0.5
) +
scale_fill_manual(values = pal, na.value = "#FFFFFF") +
# theme_bw() + theme(
# panel.border = element_blank(),
# panel.grid.major = element_blank(),
# panel.grid.minor = element_blank(),
# axis.line = element_line(colour = "black"),
# legend.title = element_blank()
# )
xlim(-11,-6) +
ylim(-8,-2) +
theme_classic() + theme(legend.text = element_text(size = 10),
legend.title =element_blank(),
legend.position = "bottom",
legend.spacing.y = unit(-0.3, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(fill = guide_legend(ncol = 2, byrow = TRUE))
ggsave("./final_plots/data_presentation/sample_focus_brain_umap_euclidean.pdf", width = 5, height = 5.5)

###Extra UMAP plot not in report
#UMAP plot based only on brain samples, thus different than the plot above.
wide_data = tmm_sample %>% select(c(target_id, metadata %>% filter(organ_name == "Brain") %>% .$ID))
seed = 4
n_epochs = 1000
n_neighbors = 15
set.seed(seed)
pca_res <-
wide_data %>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames(colnames(wide_data)[1]) %>%
scale() %>%
t() %>%
pca(nPcs = dim(.)[1])
pc_lim <-
which(pca_res@R2cum > 0.8)[1]
pc_lim_sd <-
rev(which(pca_res@sDev > 1))[1]
set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
umap(n_neighbors = n_neighbors,
n_epochs = n_epochs) %>%
#metric = "correlation") %>%
as_tibble() %>%
set_names(paste0("UMAP", 1:ncol(.))) %>%
mutate(sample = rownames(pca_res@scores)) %>%
select(sample, everything()) %>%
left_join(metadata, by = c("sample" = "ID"))
region_tissue_colors <- metadata %>% select(region_tissue_name, region_tissue_color) %>% unique()
pal <- region_tissue_colors$region_tissue_color
pal <- setNames(pal, region_tissue_colors$region_tissue_name)
# umap_res %>% ggplot(aes(UMAP1, UMAP2, color = organ_name)) +
# geom_point(alpha = 0.8) + scale_color_manual(values = pal)
umap_res %>% ggplot(aes(UMAP1, UMAP2)) +
geom_point(
aes(fill = region_tissue_name),
color = "gray25",
alpha = 0.7,
shape = 21,
size = 2,
stroke = 0.5
) + scale_fill_manual(values = pal) +
# theme_bw() + theme(
# panel.border = element_blank(),
# panel.grid.major = element_blank(),
# panel.grid.minor = element_blank(),
# axis.line = element_line(colour = "black"),
# legend.title = element_blank()
# )
theme_classic() + theme(legend.text = element_text(size = 10),
legend.title =element_blank(),
# legend.position = "bottom",
legend.spacing.y = unit(-0.3, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(fill = guide_legend(ncol = 1, byrow = TRUE))
ggsave("./final_plots/data_presentation/sample_brain_umap_euclidean_l.pdf", width = 5.5, height = 3)

##Figure 2D - grouped sample to sample correlation plots
if(file.exists("./data/final_data/spearman_sample.csv")) {
sample_tmm_spearman <- read_csv("./data/final_data/spearman_sample.csv")
} else {
sample_tmm_spearman <- tmm_sample %>%
column_to_rownames("target_id") %>%
cor(method = "spearman", use = "pairwise.complete.obs") %>%
as.data.frame() %>%
as_tibble(rownames = "sample_name")
write_csv(as.data.frame(sample_tmm_spearman) %>% as_tibble(),"./data/final_data/spearman_sample.csv")
}
Rows: 352 Columns: 353── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): sample_name
dbl (352): ventral.forebrain_male.3, ventral.forebrain_male.2, ventral.forebrain_female.3, ventral.forebrain_female...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
correlation_to_different_organs <- tibble(sample_name = c(), correlation = c())
for (sample in metadata$ID){
sample_organ <- metadata %>% filter(ID == sample) %>% .$organ_name %>% unique()
different_organ_samples <- metadata %>% filter(organ_name != sample_organ) %>% .$ID
sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% different_organ_samples) %>% select("sample_name", sample) %>% rename("correlation" = sample)
correlation_to_different_organs <- rbind(correlation_to_different_organs, sample_correlation)
}
correlation_to_same_organ <- tibble(sample_name = c(), correlation = c())
for (sample in metadata$ID){
sample_organ <- metadata %>% filter(ID == sample) %>% .$organ_name %>% unique()
same_organ_samples <- metadata %>% filter(organ_name == sample_organ) %>% filter(ID != sample) %>% .$ID
sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% same_organ_samples) %>% select("sample_name", sample) %>% rename("correlation" = sample)
correlation_to_same_organ <- rbind(correlation_to_same_organ, sample_correlation)
}
correlation_to_same_tissue <- tibble(sample_name = c(), correlation = c())
for (sample in metadata$ID){
sample_tissue <- metadata %>% filter(ID == sample) %>% .$tissue_name %>% unique()
same_tissue_samples <- metadata %>% filter(tissue_name == sample_tissue) %>% filter(ID != sample) %>% .$ID
sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% same_tissue_samples) %>% select("sample_name", sample) %>% rename("correlation" = sample)
correlation_to_same_tissue <- rbind(correlation_to_same_tissue, sample_correlation)
}
correlation_to_same_tissue_by_tissue <- tibble(sample_name = c(), correlation = c(), tissue_name = c())
for (sample in metadata$ID){
sample_tissue <- metadata %>% filter(ID == sample) %>% .$tissue_name %>% unique()
same_tissue_samples <- metadata %>% filter(tissue_name == sample_tissue) %>% .$ID
sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% same_tissue_samples) %>% select("sample_name", sample) %>% filter(sample_name != sample) %>% left_join(metadata %>% select(ID, tissue_name), by= c("sample_name" = "ID")) %>% rename("correlation" = sample)
correlation_to_same_tissue_by_tissue <- rbind(correlation_to_same_tissue_by_tissue, sample_correlation)
}
correlation_to_same_tissue_by_tissue <- correlation_to_same_tissue_by_tissue %>%
mutate(tissue_name = str_to_sentence(tissue_name)) %>%
group_by(tissue_name) %>%
mutate(min = min(correlation)) %>%
ungroup() %>%
arrange(min)
p1 <- correlation_to_different_organs %>%
ggplot(aes(correlation)) +
geom_histogram(bins = 100) + xlim(0.5,1)+
theme_classic() +
theme(panel.background = element_rect("gray90"),
axis.ticks.x = element_blank(),
axis.text.x = element_blank(),
axis.title.y.left = element_blank(),
axis.title.x = element_blank()) +
scale_y_continuous(expand = expansion(mult = 0, add = 0)) +
ggtitle("Different organs")
p2 <- correlation_to_same_organ %>%
ggplot(aes(correlation)) +
geom_histogram(bins = 100) + xlim(0.5,1) +
theme_classic() +
theme(panel.background = element_rect("gray90"),
axis.ticks.x = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank()) +
scale_y_continuous(expand = expansion(mult = 0, add = 0)) +
ggtitle("Same organs")
p3 <- correlation_to_same_tissue %>%
ggplot(aes(correlation)) +
geom_histogram(bins = 100) + xlim(0.5,1) +
theme_classic() +
theme(panel.background = element_rect("gray90"),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank()
) +
scale_y_continuous(expand = expansion(mult = 0, add = 0)) +
ggtitle("Same tissue")
p4 <- correlation_to_same_tissue_by_tissue %>%
mutate(tissue_name = factor(tissue_name, correlation_to_same_tissue_by_tissue$tissue_name %>%
unique())) %>%
ggplot(aes(x = correlation, y = tissue_name)) +
geom_boxplot(outlier.size=0.3) + xlim(0.5,1) +
theme_bw() +
xlab ("Spearman's roh") +
theme(axis.title.y = element_blank())
p1 / p2 / p3 / p4 + plot_layout(heights = c(1, 1, 1, 15))
ggsave("./final_plots/data_presentation/spearman_corr_sample_vs_tissue_organ.pdf", height = 14, width = 4)

#Specificity and distribution annotation ##Formulas
#calculate_tau_score and hpa_gene_classification taken from Max Karlsson
calculate_tau_score <-
function(wide_data) {
max_exp <-
apply(wide_data,
MARGIN = 1,
function(x) max(x, na.rm = T))
N <-
apply(wide_data,
MARGIN = 1,
function(x) length(which(!is.na(x))))
expression_sum <-
wide_data %>%
sweep(MARGIN = 1,
STATS = max_exp,
FUN = `/`) %>%
{1 - .} %>%
apply(MARGIN = 1,
function(x) sum(x, na.rm = T))
tau_score <-
(expression_sum / (N - 1)) %>%
enframe("gene", "tau_score")
tau_score
}
hpa_gene_classification <-
#feed in long data
function(data, expression_col, tissue_col, gene_col, enr_fold, max_group_n, det_lim = 1) {
data_ <-
data %>%
select(gene = gene_col,
expression = expression_col,
tissue = tissue_col) %>%
mutate(expression = round(expression, 4))
if(any(is.na(data_$expression))) stop("NAs in expression column")
if(any(is.na(data_$gene))) stop("NAs in gene column")
if(any(is.na(data_$tissue))) stop("NAs in tissue column")
n_groups <- length(unique(data_$tissue))
gene_class_info <-
data_ %>%
group_by(gene) %>%
summarise(
# Gene expression distribution metrics
mean_exp = mean(expression, na.rm = T),
min_exp = min(expression, na.rm = T),
max_exp = max(expression, na.rm = T),
max_2nd = sort(expression)[length(expression)-1],
# Expression frequency metrics
n_exp = length(which(expression >= det_lim)),
frac_exp = n_exp/length(expression[!is.na(expression)])*100,
# Limit of enhancement metrics
lim = max_exp/enr_fold,
exps_over_lim = list(expression[which(expression >= lim & expression >= det_lim)]),
n_over = length(exps_over_lim[[1]]),
mean_over = mean(exps_over_lim[[1]]),
min_over = ifelse(n_over == 0, NA,
min(exps_over_lim[[1]])),
max_under_lim = max(expression[which(expression < min_over)], det_lim*0.1),
exps_enhanced = list(which(expression/mean_exp >= enr_fold & expression >= det_lim)),
# Expression patterns
enrichment_group = paste(sort(tissue[which(expression >= lim & expression >= det_lim)]), collapse=";"),
n_enriched = length(tissue[which(expression >= lim & expression >= det_lim)]),
n_enhanced = length(exps_enhanced[[1]]),
enhanced_in = paste(sort(tissue[exps_enhanced[[1]]]), collapse=";"),
n_na = n_groups - length(expression),
max_2nd_or_lim = max(max_2nd, det_lim*0.1),
tissues_not_detected = paste(sort(tissue[which(expression < det_lim)]), collapse=";"),
tissues_detected = paste(sort(tissue[which(expression >= det_lim)]), collapse=";"))
gene_categories <-
gene_class_info %>%
mutate(
spec_category = case_when(n_exp == 0 ~ "not detected",
# Genes with expression fold times more than anything else are tissue enriched
max_exp/max_2nd_or_lim >= enr_fold ~ "tissue enriched",
# Genes with expression fold times more than other tissues in groups of max group_n - 1 are group enriched
max_exp >= lim &
n_over <= max_group_n & n_over > 1 &
mean_over/max_under_lim >= enr_fold ~ "group enriched",
# Genes with expression in tissues fold times more than the mean are tissue enhance
n_enhanced > 0 ~ "tissue enhanced",
# Genes expressed with low tissue specificity
T ~ "low tissue specificity"),
dist_category = case_when(frac_exp == 100 ~ "detected in all",
frac_exp >= 31 ~ "detected in many",
n_exp > 1 ~ "detected in some",
n_exp == 1 ~ "detected in single",
n_exp == 0 ~ "not detected"),
spec_score = case_when(spec_category == "tissue enriched" ~ max_exp/max_2nd_or_lim,
spec_category == "group enriched" ~ mean_over/max_under_lim,
spec_category == "tissue enhanced" ~ max_exp/mean_exp))
##### Rename and format
gene_categories %>%
mutate(enriched_tissues = case_when(spec_category %in% c("tissue enriched", "group enriched") ~ enrichment_group,
spec_category == "tissue enhanced" ~ enhanced_in),
n_enriched = case_when(spec_category %in% c("tissue enriched", "group enriched") ~ n_enriched,
spec_category == "tissue enhanced" ~ n_enhanced)) %>%
select(gene,
spec_category,
dist_category,
spec_score,
n_expressed = n_exp,
fraction_expressed = frac_exp,
max_exp = max_exp,
enriched_tissues,
n_enriched,
n_na = n_na,
tissues_not_detected,
tissues_detected)
}
##Annotation
# Specificity classification at consensus level
classification_consensus <- hpa_gene_classification(data = tmm_consensus_tissue, expression_col = "tmm", tissue_col = "consensus_tissue_name", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1)
consensus_tau <- calculate_tau_score(tmm_consensus_tissue %>%
spread(consensus_tissue_name, tmm)%>%
mutate_if(is.numeric, function(x){log10(x+1)})%>%
column_to_rownames("target_id"))
#category not detected has a very noisy tau, so no tau score for those
classification_consensus <- classification_consensus %>%
left_join(consensus_tau, by = c("gene" = "gene")) %>%
mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score))
#at region level
classification_region <- hpa_gene_classification(data = tmm_region_tissue, expression_col = "tmm", tissue_col = "region_tissue_name", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1)
region_tau <- calculate_tau_score(tmm_region_tissue %>%
spread(region_tissue_name, tmm) %>%
mutate_if(is.numeric, function(x){log10(x+1)}) %>%
column_to_rownames("target_id") )
classification_region <- classification_region %>%
left_join(region_tau, by = c("gene" = "gene")) %>%
mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score))
#at tissue level
classification_tissue <- hpa_gene_classification(data = tmm_tissue, expression_col = "tmm", tissue_col = "tissue_name", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1)
tissue_tau <- calculate_tau_score(tmm_tissue %>%
spread(tissue_name, tmm) %>%
mutate_if(is.numeric, function(x){log10(x+1)})%>%
column_to_rownames("target_id") )
classification_tissue <- classification_tissue %>%
left_join(tissue_tau, by = c("gene" = "gene")) %>%
mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score))
#Figure 3 ##Figure 3A - Pie charts at tissue type level
gene_category_pal <- c("Detected in all" = "#253494",
"Detected in many" = "#2c7fb8",
"Detected in some" = "#41b6c4",
"Detected in single" = "#a1dab4",
"Not detected " = " #bebebe",
"Low tissue specificity" = "grey40",
"Tissue enhanced" = "#984ea3",
"Group enriched" = "#FF9D00",
"Tissue enriched" = "#e41a1c")
plot_data <-
classification_tissue %>%
rename(specificity = spec_category, distribution = dist_category) %>%
select(gene, specificity, distribution) %>%
gather(class_type, class, -1) %>%
group_by(class_type, class) %>%
count() %>%
group_by(class_type) %>%
mutate(perc = 100 * n / sum(n),
label = paste0(n, "\n", round(perc, 1), "%")) %>%
ungroup() %>%
mutate(class = factor(str_to_sentence(class), str_to_sentence(c("tissue enriched", "group enriched", "tissue enhanced", "low tissue specificity", "detected in all", "detected in many", "detected in some", "detected in single", "not detected"))),
class_type = factor(str_to_sentence(class_type),
c("Specificity", "Distribution")))
plot_dodge = 0.1
plot_data %>%
arrange(match(class, rev(levels(class)))) %>%
group_by(class_type) %>%
mutate(y_stack = cumsum(n) - n/2) %>%
{ggplot(., aes(1, n, fill = class, group = class,
label = label)) +
geom_col(show.legend = F,
color = "white",
width = 1) +
geom_segment(aes(x = 1.5 + plot_dodge, xend = 1.5,
y = y_stack, yend = y_stack), size = 0.5) +
geom_text_repel(aes(x = 1.5 + plot_dodge, y = y_stack),
color = "black", nudge_x = plot_dodge,
segment.size = 0.5, size = 24/11) +
scale_fill_manual(values = gene_category_pal) +
facet_wrap(~class_type) +
coord_polar("y",start = 0) +
theme_void() +
scale_x_continuous(expand = expansion(c(0,0.8)))}
ggsave("./final_plots/classification/class_pies_tissue_type.pdf",width = 6, height = 5)

##Figure 3B - Distibution for each tissue type
classification_tissue %>% group_by(dist_category) %>% count()
ordered_names_distr <- classification_tissue %>%
filter(dist_category %in% c("detected in single", "detected in some", "detected in many", "detected in all")) %>%
separate_rows(tissues_detected, sep = ";") %>%
group_by(dist_category, tissues_detected) %>%
count() %>%
group_by(tissues_detected) %>%
summarise(sum = sum(n)) %>%
arrange(sum) %>%
.$tissues_detected %>%
str_to_sentence()
detection_palette <- c("Detected in all" = "#253494",
"Detected in many" = "#2c7fb8",
"Detected in some" = "#41b6c4",
"Detected in single" = "#a1dab4",
"Not detected " = "grey")
classification_tissue %>%
filter(
dist_category %in% c(
"detected in single",
"detected in some",
"detected in many",
"detected in all"
)
) %>%
separate_rows(tissues_detected, sep = ";") %>%
group_by(dist_category, tissues_detected) %>%
count() %>%
mutate(tissues_detected = factor(str_to_sentence( tissues_detected), ordered_names_distr)) %>%
mutate(dist_category = factor(
str_to_sentence( dist_category),
c(
"Detected in single",
"Detected in some",
"Detected in many",
"Detected in all"
)
)) %>%
ggplot(aes(x = n, y = tissues_detected, fill = dist_category)) +
geom_col() +
scale_fill_manual(values = detection_palette) +
theme_classic() + theme(
axis.title.y = element_blank(),
axis.line.y = element_blank(),
axis.title.x = element_blank(),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
ggtitle("Number of detected genes per tissue type") +
guides(fill = guide_legend(nrow = 2, byrow = TRUE))
ggsave("./final_plots/classification/tissue_detection_a.pdf", height = 11.5, width = 4.5)

NA
NA
##Figure 3C - Distribution and Tau score
detection_palette <- c("Detected in all" = "#253494",
"Detected in many" = "#2c7fb8",
"Detected in some" = "#41b6c4",
"Detected in single" = "#a1dab4",
"Not detected " = "grey")
p1 <- classification_tissue %>%
mutate(dist_category = factor(str_to_sentence( dist_category), levels = rev(names(detection_palette))),
enriched_tissues = str_to_sentence(enriched_tissues)) %>%
filter(dist_category != "Not detected") %>%
ggplot(aes(x = tau_score, y = dist_category, fill = dist_category)) +
geom_violin() +
scale_fill_manual(values = gene_category_pal, name = "Specificity") +
xlab("Tau score") +
theme_bw() +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank()
)
#ggsave("./final_plots/classification/tau_to_distribution.pdf",width = 5.5, height = 4)
p2 <- classification_tissue %>%
ggplot(aes(tau_score)) +
geom_histogram(bins = 100) +
theme_classic() +
ylab("Count")+
theme(panel.background = element_rect("gray90"),
#axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank()) +
scale_y_continuous(expand = expansion(mult = 0, add = 0))
p2/ p1 + plot_layout(heights = c(1, 3))
ggsave("./final_plots/classification/distribution_and_dendrogram_tissue_w_overall.pdf",width = 6.5, height = 6)

###Extra Figures not in report
detection_palette <- c("detected in all" = "#253494",
"detected in many" = "#2c7fb8",
"detected in some" = "#41b6c4",
"detected in single" = "#a1dab4",
"not detected " = "grey")
ordered_names_distr <- classification_tissue %>%
filter(dist_category %in% c("detected in single"#,
# "detected in some"
)) %>%
separate_rows(tissues_detected, sep = ";") %>%
group_by(dist_category, tissues_detected) %>%
count() %>%
group_by(tissues_detected) %>%
summarise(sum = sum(n)) %>%
arrange(sum) %>%
.$tissues_detected
classification_tissue %>%
filter(
dist_category %in% c(
"detected in single"#,
#
)
) %>%
separate_rows(tissues_detected, sep = ";") %>%
group_by(dist_category, tissues_detected) %>%
count() %>%
mutate(tissues_detected = factor(tissues_detected, ordered_names_distr)) %>%
mutate(dist_category = factor(
dist_category,
c(
"detected in single",
"detected in some",
"detected in many",
"detected in all"
)
)) %>%
ggplot(aes(x = n, y = tissues_detected, fill = dist_category)) +
geom_col() +
scale_fill_manual(values = detection_palette) +
theme_classic() + theme(
axis.title.y = element_blank(),
axis.line.y = element_blank(),
axis.title.x = element_blank(),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
# ggtitle("Number of detected genes per tissue type") +
ggtitle("Number of detected genes detected in a single tissue") +
guides(fill = guide_legend(nrow = 2, byrow = TRUE))

detection_palette <- c("detected in all" = "#253494",
"detected in many" = "#2c7fb8",
"detected in some" = "#41b6c4",
"detected in single" = "#a1dab4",
"not detected " = "grey")
ordered_names_distr <- classification_tissue %>%
filter(dist_category %in% c("detected in single",
"detected in some"
)) %>%
separate_rows(tissues_detected, sep = ";") %>%
group_by(dist_category, tissues_detected) %>%
count() %>%
group_by(tissues_detected) %>%
summarise(sum = sum(n)) %>%
arrange(sum) %>%
.$tissues_detected
classification_tissue %>%
filter(
dist_category %in% c(
"detected in single",
"detected in some"
)
) %>%
separate_rows(tissues_detected, sep = ";") %>%
group_by(dist_category, tissues_detected) %>%
count() %>%
mutate(tissues_detected = factor(tissues_detected, ordered_names_distr)) %>%
mutate(dist_category = factor(
dist_category,
c(
"detected in single",
"detected in some",
"detected in many",
"detected in all"
)
)) %>%
ggplot(aes(x = n, y = tissues_detected, fill = dist_category)) +
geom_col() +
scale_fill_manual(values = detection_palette) +
theme_classic() + theme(
axis.title.y = element_blank(),
axis.line.y = element_blank(),
axis.title.x = element_blank(),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
ggtitle("Number of detected genes per tissue type") +
# ggtitle("Number of detected genes detected in a single tissue") +
guides(fill = guide_legend(nrow = 2, byrow = TRUE))

#Figure 4 ##Figure 4A - Pie charts at grouped tissue level
gene_category_pal <- c("Detected in all" = "#253494",
"Detected in many" = "#2c7fb8",
"Detected in some" = "#41b6c4",
"Detected in single" = "#a1dab4",
"Not detected " = " #bebebe",
"Low tissue specificity" = "grey40",
"Tissue enhanced" = "#984ea3",
"Group enriched" = "#FF9D00",
"Tissue enriched" = "#e41a1c")
plot_data <-
classification_consensus %>%
rename(specificity = spec_category, distribution = dist_category) %>%
select(gene, specificity, distribution) %>%
gather(class_type, class, -1) %>%
group_by(class_type, class) %>%
count() %>%
group_by(class_type) %>%
mutate(perc = 100 * n / sum(n),
label = paste0(n, "\n", round(perc, 1), "%")) %>%
ungroup() %>%
mutate(class = factor(str_to_sentence(class), str_to_sentence(c("tissue enriched", "group enriched", "tissue enhanced", "low tissue specificity", "detected in all", "detected in many", "detected in some", "detected in single", "not detected"))),
class_type = factor(str_to_sentence(class_type),
c("Specificity", "Distribution")))
plot_dodge = 0.1
plot_data %>%
arrange(match(class, rev(levels(class)))) %>%
group_by(class_type) %>%
mutate(y_stack = cumsum(n) - n/2) %>%
{ggplot(., aes(1, n, fill = class, group = class,
label = label)) +
geom_col(show.legend = F,
color = "white",
width = 1) +
geom_segment(aes(x = 1.5 + plot_dodge, xend = 1.5,
y = y_stack, yend = y_stack), size = 0.5) +
geom_text_repel(aes(x = 1.5 + plot_dodge, y = y_stack),
color = "black", nudge_x = plot_dodge,
segment.size = 0.5, size = 24/11) +
scale_fill_manual(values = gene_category_pal) +
facet_wrap(~class_type) +
coord_polar("y",start = 0) +
theme_void() +
scale_x_continuous(expand = expansion(c(0,0.8)))}
ggsave("./final_plots/classification/class_pies_grouped_tissue.pdf",width = 6, height = 5)

##Figure 4B - Speceficity for each grouped tissue
classification_consensus %>% group_by(spec_category) %>% count()
ordered_names_sp <-
classification_consensus %>%
filter(spec_category %in% c("group enriched", "tissue enriched", "tissue enhanced")) %>%
separate_rows(enriched_tissues, sep = ";") %>%
group_by(spec_category, enriched_tissues) %>%
count() %>%
ungroup() %>%
group_by(enriched_tissues) %>%
summarise(sum = sum(n)) %>%
ungroup() %>%
arrange(sum) %>%
.$enriched_tissues %>%
str_to_sentence()
specificity_palette <- rev(c("Not detected" = "grey",
"Low tissue specificity" = "grey40",
"Tissue enhanced" = "#984ea3",
"Group enriched" = "#FF9D00",
"Tissue enriched" = "#e41a1c"))
classification_consensus %>%
filter(spec_category %in% c("group enriched", "tissue enriched", "tissue enhanced")) %>%
separate_rows(enriched_tissues, sep = ";") %>%
group_by(spec_category, enriched_tissues) %>%
count() %>%
mutate(enriched_tissues = factor(str_to_sentence(enriched_tissues), ordered_names_sp)) %>%
mutate(spec_category = factor(str_to_sentence(spec_category), c("Tissue enriched", "Group enriched", "Tissue enhanced"))) %>%
ggplot(aes(x = n, y = enriched_tissues, fill = spec_category)) +
geom_col() +
scale_fill_manual(values = specificity_palette) +
xlab("Number of genes")+
theme_classic() + theme(
axis.title.y = element_blank(),
# axis.title.x = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
ggtitle("Specificity category per consensus tissue")
ggsave("./final_plots/classification/consensus_tissue_specificity.pdf", height = 7, width = 5.5)

##Figure 4C - Specificity and Tau score
specificity_palette <- rev(c("Not detected" = "grey",
"Low tissue specificity" = "grey40",
"Tissue enhanced" = "#984ea3",
"Group enriched" = "#FF9D00",
"Tissue enriched" = "#e41a1c"))
p1 <- classification_consensus %>%
mutate(spec_category = factor(str_to_sentence( spec_category), levels = names(specificity_palette)),
enriched_tissues = str_to_sentence(enriched_tissues)) %>%
filter(spec_category != "Not detected") %>%
ggplot(aes(x = tau_score, y = spec_category, fill = spec_category)) +
geom_violin() +
scale_fill_manual(values = gene_category_pal, name = "Specificity") +
xlab("Tau score") +
theme_bw() +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank()
)
#ggsave("./final_plots/classification/tau_to_specificity.pdf",width = 5.5, height = 4)
p2 <- classification_consensus %>%
ggplot(aes(tau_score)) +
geom_histogram(bins = 100) +
theme_classic() +
ylab("Count")+
theme(panel.background = element_rect("gray90"),
#axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank()) +
scale_y_continuous(expand = expansion(mult = 0, add = 0))
p2/ p1 + plot_layout(heights = c(1, 3))
ggsave("./final_plots/classification/specificity_and_dendrogram_consensus_w_overall.pdf",width = 7, height = 6)

##Figure 4D - Gene annotation alluvial plot
width = 0.1
alluv_1 <-
classification_consensus %>%
mutate(Specificity = str_to_sentence(spec_category),
Distribution = str_to_sentence(dist_category)) %>%
select(Specificity,
Distribution) %>%
mutate(row_n = row_number()) %>%
gather(bar, chunk, -row_n) %>%
mutate(color_vars = 1) %>%
group_by(row_n) %>%
mutate(chunk_color = chunk[match(c("Specificity",
"Distribution")[color_vars], bar)]) %>%
ungroup() %>%
mutate(chunk = factor(chunk, levels = c('Tissue enriched', 'Group enriched',
'Tissue enhanced', 'Low tissue specificity',
'Detected in single',
'Detected in some',
'Detected in many',
'Detected in all',
'Not detected')),
bar = factor(bar, levels = c("Specificity",
"Distribution"))) %>%
ggplot(aes(x = bar, stratum = chunk, alluvium = row_n,
y = 1)) +
geom_flow(aes(fill = chunk_color),
show.legend = F, width = width,
knot.pos = 1/6) +
geom_stratum(aes(fill = chunk),
show.legend = F, color = NA, width = width) +
scale_x_discrete(expand = c(.1, .1), position = "top") +
scale_fill_manual(values = c(gene_category_pal)) +
theme(axis.text.y = element_text(size = 18, face = "bold"),
axis.text.x = element_blank(),
axis.ticks = element_blank(),
panel.background = element_blank(),
axis.title = element_blank()) +
coord_flip()
# alluv_1
flow_data <-
ggplot_build(alluv_1)$data[[1]] %>%
as_tibble() %>%
{
if ("side" %in% names(.)) {
.
} else{
mutate(.,
side = case_when(flow == "from" ~ "start",
flow == "to" ~ "end"))
}
}
stratum_data <-
ggplot_build(alluv_1)$data[[2]]
flow_data_labels <-
flow_data %>%
as_tibble() %>%
select(x, stratum, group, side, ymin, ymax) %>%
pivot_wider(names_from = side,
values_from = c(x, stratum, ymin, ymax)) %>%
mutate_at(
c(
"x_end",
"ymax_end",
"ymin_end",
"x_start",
"ymax_start",
"ymin_start"
),
as.numeric
) %>%
group_by(stratum_start, stratum_end, x_start, x_end) %>%
summarise(
y_end = (min(ymin_end) + max(ymax_end)) / 2,
y_start = (min(ymin_start) + max(ymax_start)) / 2,
size = max(ymax_start) - min(ymin_start)
)
`summarise()` has grouped output by 'stratum_start', 'stratum_end', 'x_start'. You can override using the `.groups` argument.
alluv_2 <-
alluv_1 +
geom_text(data = flow_data_labels,
aes(x = x_start + width/2,
y = y_start,
label = size),
inherit.aes = F,
size = 3,
angle = -90,
hjust = 1,
vjust = 0.5) +
geom_text(data = flow_data_labels,
aes(x = x_end - width/2,
y = y_end,
label = size),
inherit.aes = F,
size = 3,
angle = -90,
hjust = 0,
vjust = 0.5) +
# Stratum label
geom_text(data = stratum_data %>%
filter(x == 1),
aes(x = x - width/2,
y = y,
label = stratum),
size = 4,
vjust = 1.5,
inherit.aes = F) +
geom_text(data = stratum_data %>%
filter(x == 2),
aes(x = x + width/2,
y = y,
label = stratum),
size = 4,
vjust = -0.5,
inherit.aes = F) +
geom_text(data = stratum_data,
aes(x = x,
y = y,
label = ymax - ymin),
size = 4,
fontface = "bold",
color = "white",
inherit.aes = F)
alluv_2
ggsave("./final_plots/classification/alluvial_classification.pdf",width = 8, height = 3)

###Extra figures not in report
organ_colors <- metadata %>% select(consensus_tissue_name, consensus_tissue_color) %>% unique()
pal <- organ_colors$consensus_tissue_color
pal <- setNames(pal, str_to_sentence(organ_colors$consensus_tissue_name))
specificity_palette <- rev(c("Not detected" = "grey",
"Low tissue specificity" = "grey40",
"Tissue enhanced" = "#984ea3",
"Group enriched" = "#FF9D00",
"Tissue enriched" = "#e41a1c"))
class_table_temp <-
classification_consensus %>%
select(gene, spec_category, enriched_tissues) %>%
separate_rows(enriched_tissues, sep = ";") %>%
mutate(spec_category = factor(str_to_sentence( spec_category), levels = names(specificity_palette)),
enriched_tissues = str_to_sentence(enriched_tissues))
plot_dendro <-
tmm_consensus_tissue %>%
select(target_id, consensus_tissue_name, tmm) %>%
mutate(consensus_tissue_name = str_to_sentence(consensus_tissue_name)) %>%
spread(target_id, tmm) %>%
column_to_rownames("consensus_tissue_name") %>%
t() %>%
cor(method = "spearman") %>%
{1 - .} %>%
as.dist() %>%
hclust(method = "average") %T>%
plot %>%
dendro_data()

dendro_plot_data <-
left_join(plot_dendro$segments,
plot_dendro$labels,
by = c("x" = "x", "yend" = "y"))
left_plot <-
dendro_plot_data %>%
ggplot() +
geom_segment(aes(x=y, y=x, xend=yend, yend=xend, group = label))+
geom_rect(aes(xmin=0, ymin=x + 0.5,
xmax=-0.02, ymax=xend - 0.5,
fill = label),
show.legend = F) +
scale_color_manual(values = pal)+
scale_fill_manual(values = pal)+
scale_x_reverse(expand = expansion(0), position = "top")+
scale_y_continuous(expand = expansion()) +
xlab("1 - Spearman's rho") +
theme(axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(1,1,1,1), units = "mm"),
panel.background = element_blank())
right_plot <-
class_table_temp %>%
filter(!is.na(enriched_tissues)) %>%
group_by(enriched_tissues, spec_category) %>%
summarise(n_genes = n()) %>%
ungroup() %>%
mutate(enriched_tissues = factor(enriched_tissues, levels = plot_dendro$labels$label),
spec_category = factor(spec_category, names(specificity_palette))) %>%
ggplot(aes(n_genes, enriched_tissues, fill = spec_category)) +
geom_col(width = 0.8, size = 0.1) +
theme_bw() +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
scale_fill_manual(values = gene_category_pal, name = "Specificity") +
# coord_flip() +
xlab("Number of genes") +
scale_x_continuous(position = "top", expand = expansion(c(0, 0.1))) +
scale_y_discrete(expand = expansion()) +
theme(axis.text.y = element_text(hjust = 0.5),
legend.position = c(0.7, 0.5),
axis.title.y = element_blank(),
panel.border = element_blank())
`summarise()` has grouped output by 'enriched_tissues'. You can override using the `.groups` argument.
left_plot + right_plot +
plot_layout(widths = c(0.3, 1))
ggsave("./final_plots/classification/specificity_and_dendrogram_consensus.pdf",width = 7, height = 7)

#Figure 5
##Plot based on Max Karlsson's network plot
classification_network_plot_2 <-
function(class_table, gene_col, spec_col, enriched_col, spec_filter, pal, savename, enriched_sep = ";",
node_filter_rank = 2, node_filter_show_cat = c("tissue enriched"),
node_filter_min = 2, node_filter_n_show = 8, scale_factor = 1) {
enrichment_table <-
class_table %>%
select(gene = gene_col,
spec = spec_col,
enriched = enriched_col) %>%
filter(spec %in% spec_filter) %>%
group_by(enriched, spec) %>%
summarise(n_genes = n()) %>%
ungroup()
net_data <-
enrichment_table %>%
mutate(all_enriched = enriched) %>%
separate_rows(enriched, sep = enriched_sep) %>%
group_by(enriched, spec) %>%
mutate(rank = rank(-n_genes, ties.method = "min")) %>%
group_by(all_enriched) %>%
mutate(any_low_rank = any(rank <= node_filter_rank)) %>%
ungroup() %>%
filter((spec == node_filter_show_cat | any_low_rank | n_genes >= node_filter_n_show) &
(spec == node_filter_show_cat | n_genes >= node_filter_min)) %>%
mutate(edge_id = paste("enriched:", all_enriched)) %>%
arrange(n_genes)
net_edges <-
net_data %$%
tibble(node1 = enriched, node2 = edge_id, n = n_genes) %>%
unique()
g <-
net_edges %>%
graph_from_data_frame(directed = FALSE) %>%
ggraph(layout = "kk")
link_map <-
net_edges %>%
gather(node, id, -(3)) %>%
mutate(tissue_node = node == "node1",
color_id = case_when(tissue_node ~ id,
grepl(";", id) ~ "Group enriched",
!grepl(";", id) ~ "Tissue enriched"),
label = ifelse(tissue_node, color_id, n)) %>%
select(n, node, id, tissue_node, color_id, label) %>%
unique()
edge_data <- get_edges()(g$data)
node_data <-
get_nodes()(g$data) %>%
as_tibble() %>%
left_join(link_map,
by = c("name" = "id"))
fig <-
g +
geom_edge_arc(aes(width = n),
color = "gray",
strength = 0,
alpha = 0.5,
show.legend = F) +
scale_edge_alpha_continuous(range = c(0.3, 1)) +
scale_edge_width_continuous(range = c(1, 3)) +
geom_node_point(data = node_data %>%
filter(!tissue_node),
aes(size = log10(n),
fill = color_id),
stroke = 1,
# size = 10,
shape = 21,
show.legend = F)+
geom_node_point(data = node_data %>%
filter(tissue_node),
aes(fill = color_id),
stroke = 1,
size = 20 * scale_factor,
shape = 21,
show.legend = F)+
geom_node_text(data = node_data,
aes(label = label),
size = 4 * scale_factor) +
scale_size_continuous(range = c(5, 10) * scale_factor) +
scale_fill_manual(values = pal) +
theme_void()
## ----- Save
cyto_summary <-
net_edges %>%
mutate(category = ifelse(!grepl(enriched_sep, node2), "Tissue enriched", "Group enriched"),
node_id = unclass(factor(node2)),
node1 = str_to_sentence(node1),
n_sqrt = sqrt(n),
str_len = str_length(node1)) %>%
select(category, node1, node2, node_id, n, n_sqrt, str_len)
cyto_summary %>%
write_delim("cytoscape nodes summary.txt", delim = "\t")
# write_delim(savepath(paste(savename, "cytoscape nodes summary.txt")), delim = "\t")
bind_rows(cyto_summary %>%
left_join(pal %>%
enframe("node1", "color")) %>%
select(node_id = node1,
color) %>%
unique() %>%
mutate(node_type = "Tissue"),
cyto_summary %>%
mutate(color = case_when(category == "Tissue enriched" ~ "#e41a1c",
category == "Group enriched" ~ "#FF9D00"),
node_id = as.character(node_id)) %>%
select(node_id, color) %>%
unique() %>%
mutate(node_type = "Enrichment")) %>%
mutate(color2 = case_when(node_type == "Enrichment" ~ color,
node_type == "Tissue" ~ "#D3D3D3FF"),
color3 = case_when(node_type == "Enrichment" ~ color,
node_type == "Tissue" ~ "#BEBEBEFF")) %>%
write_delim("cytoscape nodes color.txt", delim = "\t")
#write_delim(savepath(paste(savename, "cytoscape nodes color.txt")), delim = "\t")
bind_rows(cyto_summary %>%
select(node_id = node1) %>%
mutate(label = node_id) %>%
unique(),
cyto_summary %>%
mutate(node_id = as.character(node_id),
label = as.character(n)) %>%
select(node_id, label) %>%
unique()) %>%
write_delim("cytoscape nodes label whole group.txt",
#write_delim(savepath(paste(savename, "cytoscape nodes label whole group.txt")),
delim = "\t")
## ----
fig
}
organ_colors <- metadata %>% select(consensus_tissue_name, organ_color) %>% unique()
pal1 <- organ_colors$organ_color
pal1 <- setNames(pal1, organ_colors$consensus_tissue_name)
specificity_palette <- rev(c("Not detected" = "grey",
"Tissue" = "grey",
"Low tissue specificity" = "grey40",
"Tissue enhanced" = "#984ea3",
"Group enriched" = "#FF9D00",
"Tissue enriched" = "#e41a1c"))
library(influential)
classification_network_plot_2(
class_table = classification_consensus %>% mutate(spec_category = str_to_sentence(spec_category)),
gene_col = "gene",
spec_col = "spec_category",
enriched_col = "enriched_tissues",
spec_filter = c("Tissue enriched", "Group enriched"),
pal = c(pal1, specificity_palette),
savename = "test_interconsensus",
enriched_sep = ";",
node_filter_rank = 2,
node_filter_show_cat = c("Tissue enriched"),
node_filter_min = 2,
node_filter_n_show = 5,
scale_factor = 0.8
)
`summarise()` has grouped output by 'enriched'. You can override using the `.groups` argument.Joining, by = "node1"
ggsave("./final_plots/data_presentation/nework_plot2-filter.pdf", height = 20, width = 20)

#Read Comparison data
comp_metadata <- read_csv("./data/final_data/comparison_metadata-init-1-0.csv")
Rows: 124 Columns: 14── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (13): tissue_name, region_tissue_name, consensus_tissue_name, organ_name, tissue_color, region_tissue_color, co...
lgl (1): regional_tissue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
human_atlas_tissue <-
read_delim("./data/human_hpa/rna_tissue_consensus.tsv", delim = "\t") %>%
mutate(source = "hpa_consensus") %>%
group_by(Gene) %>%
mutate(nx = case_when(nTPM == 0 ~ 0,
T ~ nTPM / sqrt(sd(nTPM)))) %>%
ungroup() #%>%
Rows: 1084208 Columns: 4── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): Gene, Gene name, Tissue
dbl (1): nTPM
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# # just necessary if read data of multiple different sources, then would make sense to average out samples named the same
# # just from one source atm, so no need for this.
# group_by(Tissue, Gene) %>%
# summarise(nTPM = mean(nTPM, na.rm = T)) %>%
# ungroup()
hpa_comp <- human_atlas_tissue %>% left_join(
comp_metadata %>%
select(hpa_consensus_name, comparison_tissue_name) %>%
distinct() %>%
filter(!is.na(hpa_consensus_name)) %>%
filter(!is.na(comparison_tissue_name)),
by = c("Tissue" = "hpa_consensus_name")) %>%
filter(!is.na(comparison_tissue_name)) %>%
group_by(Gene,comparison_tissue_name) %>%
summarise(tmm = max(nTPM),
nx = max(nx)) %>%
ungroup() %>%
rename(tissue = comparison_tissue_name)
`summarise()` has grouped output by 'Gene'. You can override using the `.groups` argument.
rat_tissue <-
read_csv("./data/final_data/final_tmm_tissue_name.csv") %>%
# #omit gather, data should be already in long format
# gather(sample, tmm,-1) %>%
group_by(target_id) %>%
mutate(nx = case_when(tmm == 0 ~ 0,
T ~ tmm / sqrt(sd(tmm)))) %>%
ungroup()
Rows: 2224500 Columns: 3── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): target_id, tissue_name
dbl (1): tmm
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# select tissues and pool tissues that will get compared
rat_tissue_comp <- rat_tissue %>% left_join(comp_metadata %>%
select(tissue_name, comparison_tissue_name), by = c("tissue_name" = "tissue_name")) %>%
filter(!is.na(comparison_tissue_name)) %>%
group_by(target_id, comparison_tissue_name) %>%
summarise(tmm = max(tmm),
nx = max(nx)) %>%
ungroup() %>%
rename(tissue = comparison_tissue_name)
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
#check if all have the same tissue
all(rat_tissue_comp %>% distinct(tissue) == hpa_comp %>% distinct(tissue) )
[1] TRUE
#Read ortholog data
#Select either "HPA" or "ensemble"
ortholog_data_from <- "HPA"
#ortholog_data_from <- "ensemble"
include_many2many <- TRUE
include_one2many <- TRUE
#Only needed for ensembl:
#define ensemble version
version <- 103
organism_db <- "rnorvegicus_gene_ensembl"
#mart <- useEnsembl ( biomart="genes" , dataset=organism_db , version=version )
if (ortholog_data_from == "HPA"){
homolog_data <- read.table("./data/human_hpa/kalle/ensembl_rat_ortholog.tsv", sep = "\t", header = TRUE) %>%
filter(ensrnog_id %in% rat_tissue$target_id) %>%
filter(ensg_id %in% human_atlas_tissue$Gene)
} else if (ortholog_data_from == "ensemble"){
homolog_data <- getBM( attributes=c( "ensembl_gene_id", "hsapiens_homolog_ensembl_gene", "hsapiens_homolog_orthology_type") , mart=mart ) %>%
filter(hsapiens_homolog_ensembl_gene != "") %>%
filter(ensembl_gene_id %in% rat_tissue$target_id) %>%
filter(hsapiens_homolog_ensembl_gene %in% human_atlas_tissue$Gene) %>%
rename(ensrnog_id = ensembl_gene_id, ensg_id = hsapiens_homolog_ensembl_gene, ortholog_type = hsapiens_homolog_orthology_type)
}
if (include_many2many == FALSE){
homolog_data <- homolog_data %>% filter(!ortholog_type == "ortholog_many2many")
}
if (include_one2many == FALSE){
homolog_data <- homolog_data %>% filter(!ortholog_type == "ortholog_one2many")
}
#Figure 6
comparison_colors_tbl <- rbind(
comp_metadata %>% select(name = comparison_tissue_name, color = consensus_tissue_color) %>% distinct(),
comp_metadata %>% select(name = tissue_name, color = tissue_color) %>% distinct()
) %>%
distinct() %>%
drop_na() %>%
mutate(name = str_to_sentence(name))
pal <- comparison_colors_tbl$color
pal <- setNames(pal, str_to_sentence(comparison_colors_tbl$name))
plot_data1 <-
comp_metadata %>%
select(tissue_name, comparison_tissue_name, organ_name) %>%
unique() %>%
drop_na() %>%
mutate(tissue_name = str_to_sentence(tissue_name), comparison_tissue_name = str_to_sentence(comparison_tissue_name)) %>%
arrange(organ_name, comparison_tissue_name) %>%
mutate(plot_order = row_number()) %>% select(-organ_name)
plot_data2 <-
plot_data1 %>%
gather(column, label, -plot_order) %>%
group_by(label, column) %>%
summarise(plot_order = mean(plot_order)) %>%
ungroup() %>%
mutate(label = label,
column = factor(column,
c("tissue_name",
"comparison_tissue_name"
)))
`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
plot_data3 <-
plot_data1 %>%
group_by(comparison_tissue_name) %>%
mutate(left_pos = mean(plot_order))
plot_data4 <-
plot_data2 %>%
left_join(comparison_colors_tbl,
by = c("label" = "name")) %>%
group_by(column, label) %>%
summarise(miny = min(plot_order) - 0.5,
maxy = max(plot_order) + 0.5)
`summarise()` has grouped output by 'column'. You can override using the `.groups` argument.
ggplot() +
geom_rect(data = plot_data4,
aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, fill = label),
show.legend = F
) +
geom_rect(data = plot_data4 %>% filter (column == "comparison_tissue_name"),
# aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color),
aes(xmin = 2, xmax = 2.5, ymin = miny, ymax = maxy, fill = label),
show.legend = F
) +
geom_rect(data = plot_data4 %>% filter (column == "tissue_name"),
# aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color),
aes(xmin = 0.5, xmax = 1, ymin = miny, ymax = maxy, fill = label),
show.legend = F, width = 10
) +
geom_segment(
data = plot_data3,
aes(
x = "comparison_tissue_name",
xend = "tissue_name",
y = left_pos,
yend = plot_order,
color = tissue_name
),
show.legend = F,
alpha = 0.5,
size = 2
)+
geom_text(
data = plot_data2 %>% filter(column == "comparison_tissue_name"),
aes(x = column, y = plot_order, label = label),
hjust = 0,
size = 2 * 5 / 6,
label.padding = unit(0, "mm")
) +
geom_text(
data = plot_data2 %>% filter(column == "tissue_name"),
aes(x = column, y = plot_order, label = label),
hjust = 1,
size = 2 * 5 / 6,
show.legend = F,
label.padding = unit(0, "mm")
) +
# geom_label(
# data = plot_data2 %>%
# filter(column == "organ_name"),
# #aes(x = column, y = plot_order, label = label),
# aes(x = column, y = plot_order, label = gsub(" ", "\n", label)),
# show.legend = F,
# label.size = 0,
# hjust = 1,
# lineheight = 0.7,
# label.padding = unit(0, "mm"),
# size = 2 * 5 / 6
# ) +
scale_y_reverse() +
scale_x_discrete(labels = c("Rat tissue", "Comparison tissue"),position = "top") +
scale_fill_manual(values = pal) +
scale_color_manual(values = pal) +
theme_void() +
theme(axis.text.x = element_text())
Warning: Ignoring unknown parameters: `width`Warning: Ignoring unknown parameters: `label.padding`Warning: Ignoring unknown parameters: `label.padding`
ggsave("final_plots/comparison/comparison_tissues_rat.pdf", height = 5, width = 4)

comparison_colors_tbl <- rbind(
comp_metadata %>% select(name = comparison_tissue_name, color = consensus_tissue_color) %>% distinct(),
comp_metadata %>% select(name = hpa_consensus_name, color = tissue_color) %>% distinct()
) %>%
distinct() %>%
drop_na() %>%
mutate(name = str_to_sentence(name))
pal <- comparison_colors_tbl$color
pal <- setNames(pal, str_to_sentence(comparison_colors_tbl$name))
plot_data1 <-
comp_metadata %>%
select(hpa_consensus_name, comparison_tissue_name, organ_name) %>%
unique() %>%
drop_na() %>%
mutate(hpa_consensus_name = str_to_sentence(hpa_consensus_name), comparison_tissue_name = str_to_sentence(comparison_tissue_name)) %>%
arrange(organ_name, comparison_tissue_name) %>%
mutate(plot_order = row_number()) %>% select(-organ_name)
plot_data2 <-
plot_data1 %>%
gather(column, label, -plot_order) %>%
group_by(label, column) %>%
summarise(plot_order = mean(plot_order)) %>%
ungroup() %>%
mutate(label = label,
column = factor(column,
c("comparison_tissue_name", "hpa_consensus_name"
)))
`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
plot_data3 <-
plot_data1 %>%
group_by(comparison_tissue_name) %>%
mutate(left_pos = mean(plot_order))
plot_data4 <-
plot_data2 %>%
left_join(comparison_colors_tbl,
by = c("label" = "name")) %>%
group_by(column, label) %>%
summarise(miny = min(plot_order) - 0.5,
maxy = max(plot_order) + 0.5)
`summarise()` has grouped output by 'column'. You can override using the `.groups` argument.
ggplot() +
geom_rect(data = plot_data4,
aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, fill = label),
show.legend = F
) +
geom_rect(data = plot_data4 %>% filter (column == "hpa_consensus_name"),
# aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color),
aes(xmin = 2, xmax = 2.5, ymin = miny, ymax = maxy, fill = label),
show.legend = F
) +
geom_rect(data = plot_data4 %>% filter (column == "comparison_tissue_name"),
# aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color),
aes(xmin = 0.5, xmax = 1, ymin = miny, ymax = maxy, fill = label),
show.legend = F, width = 10
) +
geom_segment(
data = plot_data3,
aes(
x = "comparison_tissue_name",
xend = "hpa_consensus_name",
y = left_pos,
yend = plot_order,
color = hpa_consensus_name
),
show.legend = F,
alpha = 0.5,
size = 2
)+
geom_text(
data = plot_data2 %>% filter(column == "hpa_consensus_name"),
aes(x = column, y = plot_order, label = label),
hjust = 0,
size = 2 * 5 / 6,
label.padding = unit(0, "mm")
) +
geom_text(
data = plot_data2 %>% filter(column == "comparison_tissue_name"),
aes(x = column, y = plot_order, label = label),
hjust = 1,
size = 2 * 5 / 6,
show.legend = F,
label.padding = unit(0, "mm")
) +
# geom_label(
# data = plot_data2 %>%
# filter(column == "organ_name"),
# #aes(x = column, y = plot_order, label = label),
# aes(x = column, y = plot_order, label = gsub(" ", "\n", label)),
# show.legend = F,
# label.size = 0,
# hjust = 1,
# lineheight = 0.7,
# label.padding = unit(0, "mm"),
# size = 2 * 5 / 6
# ) +
scale_y_reverse() +
scale_x_discrete(labels = c("Comparison tissue", "Human Tissue"),position = "top") +
scale_fill_manual(values = pal) +
scale_color_manual(values = pal) +
theme_void() +
theme(axis.text.x = element_text())
Warning: Ignoring unknown parameters: `width`Warning: Ignoring unknown parameters: `label.padding`Warning: Ignoring unknown parameters: `label.padding`
ggsave("final_plots/comparison/comparison_tissues_human_rev.pdf", height = 5, width = 4)

#Batch Correction
#Do batch correction
#join rat and human tmm data together
joined_atlas_comparison_temp <- rat_tissue_comp %>%
inner_join(homolog_data, by = c("target_id" = "ensrnog_id")) %>%
unite(mutual_id, target_id, ensg_id, sep = "_") %>%
mutate(species = "rat") %>%
select(mutual_id, tissue, species , tmm) %>%
bind_rows(
hpa_comp %>%
inner_join(homolog_data, by = c("Gene" = "ensg_id")) %>%
unite(mutual_id, ensrnog_id, Gene, sep = "_") %>%
mutate(species = "human") %>%
select(mutual_id, tissue, species , tmm)
)
#### if tmm is under 0 then it is equal to 0
#mutate(tmm = ifelse(tmm < 1, 0, tmm ))
#long table with inf of on tissue and species
joined_atlas_comparison_temp_2 <- joined_atlas_comparison_temp %>%
unite(id, tissue, species, sep = "_") %>%
separate(id, into = c("tissue", "species"), sep = "_", remove = F)
#wide table only with tmm
joined_atlas_comparison_temp3_tmm <-
joined_atlas_comparison_temp_2 %>%
select(mutual_id, id, tmm) %>%
spread(id, tmm) %>%
column_to_rownames("mutual_id")
#log scale
joined_atlas_comparison_temp3_limma <-
joined_atlas_comparison_temp3_tmm %>%
{log10(. + 1)} %>%
limma::removeBatchEffect(batch = colnames(joined_atlas_comparison_temp3_tmm) %>%
str_extract("_.*$"))
#put them together in the long format
joined_atlas_comparison<-
joined_atlas_comparison_temp_2 %>%
left_join(joined_atlas_comparison_temp3_tmm %>%
as_tibble(rownames = "mutual_id") %>%
gather(id, tmm, -1)) %>%
left_join(joined_atlas_comparison_temp3_limma %>%
as_tibble(rownames = "mutual_id") %>%
gather(id, limma_log1p_tmm, -1))
Joining, by = c("mutual_id", "id", "tmm")Joining, by = c("mutual_id", "id")
#Figure 7 ##Figure 7A - Cross species dendrogramm
hclust4RNAseq <- function(df, correlation_method = "spearman"){
#wide dataframe as input
#to get correlation between samples, where rows are genes columns are samples
#to get correlation between genes across samples, input df with genes as columns
#can use later for dendogram making: ggdendrogram([hclust4RNAseq_results], rotate = FALSE, size = 10, face = "bold")
similarity <- cor(df, method=correlation_method, use="pairwise.complete.obs")
dissimilarity <- 1 - similarity
hcl <- hclust(as.dist(dissimilarity), "average")
return (hcl)
}
organ_colors <- comp_metadata %>% select(comparison_tissue_name, consensus_tissue_color) %>% drop_na() %>% distinct()
pal <- organ_colors$consensus_tissue_color
pal <- setNames(pal, str_to_sentence(organ_colors$comparison_tissue_name))
shape_def <- c(21, 22)
shape_def <- setNames(shape_def, c("Human", "Rat"))
plot_comp_dendro_data <- joined_atlas_comparison %>%
select(mutual_id, id, limma_log1p_tmm) %>%
spread(id, limma_log1p_tmm) %>%
column_to_rownames("mutual_id") %>%
cor(method = "spearman", use="pairwise.complete.obs") %>%
{1 - .} %>%
as.dist() %>%
hclust(method = "average") %T>%
plot %>%
dendro_data()

dendro_plot_data <-
left_join(plot_comp_dendro_data$segments,
plot_comp_dendro_data$labels,
by = c("x" = "x", "yend" = "y")) %>%
separate(label, c("tissue", "species"), sep = "_", remove = FALSE) %>%
mutate(tissue = str_to_sentence(tissue), species = str_to_sentence(species)) %>%
mutate(species = factor(species, c("Human", "Rat")))
left_plot <-
dendro_plot_data %>%
ggplot() +
geom_segment(aes(x=y, y=x, xend=yend, yend=xend))+
# geom_rect(aes(xmin=0, ymin=x + 0.5,
# xmax=-0.02, ymax=xend - 0.5,
# fill = tissue),
# show.legend = F) +
geom_point(aes(
x = 0,
y = x,
shape = species,
fill = tissue
),
color = "gray25",
alpha = 1,
size = 3,
stroke = 0.7
) +
geom_text(aes(x=-0.02, y=x,
label = tissue),
hjust = 0,
show.legend = F) +
scale_shape_manual(values = shape_def ) +
# scale_color_manual(values = pal) +
scale_fill_manual(values = pal, guide = "none") +
scale_x_reverse(
expand = expansion(0.5 ),
position = "top")+
scale_y_continuous(expand = expansion(0.01)) +
xlab("1 - Spearman's rho") +
theme(axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
legend.title=element_blank(),
plot.margin = unit(c(1,1,1,1), units = "mm"),
panel.background = element_blank())
left_plot
ggsave("./final_plots/comparison/comparison_dendrogram.pdf", width = 6.5, height = 9 )

##Figure 7B - Cros species UMAP
library(plotly)
library(ggplotify)
library(geomtextpath)
comp_metadata <- read_csv("./data/final_data/comparison_metadata-init-1-0.csv")
Rows: 124 Columns: 14── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (13): tissue_name, region_tissue_name, consensus_tissue_name, organ_name, tissue_color, region_tissue_color, co...
lgl (1): regional_tissue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
umap_meta <- comp_metadata %>% select(consensus_tissue_color, organ_color, comparison_tissue_name) %>% filter(comparison_tissue_name != "" ) %>% distinct()
wide_data <- joined_atlas_comparison %>%
select(-tissue, -species, -tmm) %>%
spread(id, limma_log1p_tmm)
seed <- 42
filter_zero_sd = F
n_epochs = 1000
n_neighbors = 15
pca_res <-
wide_data %>%
#no log1p because limma value is already calculated from log scale value
column_to_rownames(colnames(wide_data)[1]) %>%
scale() %>%
t() %>%
pca(nPcs = dim(.)[1])
pc_lim <-
which(pca_res@R2cum > 0.8)[1]
pc_lim_sd <-
rev(which(pca_res@sDev > 1))[1]
n_neighbors = 15
set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
uwot::umap(n_neighbors = n_neighbors,
n_epochs = n_epochs) %>%
as_tibble() %>%
set_names(paste0("UMAP", 1:ncol(.))) %>%
mutate(sample = rownames(pca_res@scores)) %>%
select(sample, everything()) %>%
separate(
sample,
into = c("tissue", "species"),
sep = "_",
remove = F
) %>%
left_join(umap_meta, by = c("tissue" = "comparison_tissue_name"))
organ_colors <-
umap_meta %>% select(comparison_tissue_name, consensus_tissue_color) %>% unique()
pal <- organ_colors$consensus_tissue_color
pal <- setNames(pal, organ_colors$comparison_tissue_name)
shape_def <- c(21, 22)
shape_def <- setNames(shape_def, c("human", "rat"))
# umap_res %>% ggplot(aes(UMAP1, UMAP2, color = organ_name)) +
# geom_point(alpha = 0.8) + scale_color_manual(values = pal)
p <- umap_res %>%
ggplot(aes(UMAP1, UMAP2)) +
geom_textpath(
aes(label = str_to_sentence(tissue)),
hjust = 0.5,
vjust = -0.3,
color = "gray20"
) +
geom_point(
aes(fill = tissue, shape = species),
color = "gray25",
alpha = 1,
size = 2,
stroke = 0.7
) +
guides(fill = "none") +
scale_fill_manual(values = pal) +
scale_shape_manual(values = shape_def) +
theme_classic() + theme(legend.text = element_text(size = 10),
#legend.title =element_blank(),
legend.spacing.y = unit(-0.1, 'cm'),
legend.spacing.x = unit(-0.01, 'cm')) +
guides(shape = guide_legend(ncol = 1, byrow = TRUE, title = "Species"))
p
if (include_many2many == TRUE & include_one2many == TRUE) {
comp_umap_file_name = paste0("./final_plots/comparison/",
ortholog_data_from,
"_umap.pdf")
} else if (include_many2many == FALSE & include_one2many == TRUE) {
comp_umap_file_name = paste0("./final_plots/comparison/",
ortholog_data_from,
"_umap.pdf")
} else if (include_many2many == FALSE &
include_one2many == FALSE) {
comp_umap_file_name = paste0("./final_plots/comparison/",
ortholog_data_from,
"_umap.pdf")
}
ggsave(comp_umap_file_name, height = 5.5, width = 6.5)

##Figure 7C - Cross species hypergeometric test
#Based on Max Karlsson: https://github.com/maxkarlsson/Pig-Atlas/blob/master/scripts/functions_classification.R
library(viridis)
library(ggsci)
class1 <- hpa_gene_classification(data = rat_tissue_comp %>% select(target_id, tissue, tmm), expression_col = "tmm", tissue_col = "tissue", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1) %>% rename(ensrnog_id = gene)
class2 <- hpa_gene_classification(data = hpa_comp %>% select(Gene, tissue, tmm), expression_col = "tmm", tissue_col = "tissue", gene_col = "Gene", enr_fold = 4, max_group_n = 5, det_lim = 1) %>%
rename(ensg_id = gene)
gene_col1 <- "ensrnog_id"
gene_col2 <- "ensg_id"
sep <- ";"
class1_long <-
class1 %>%
select(gene1 = gene_col1, enriched_tissues) %>%
mutate(
enriched_tissues = ifelse(is.na(enriched_tissues),
"not enriched",
enriched_tissues),
enriched1 = T
) %>%
separate_rows(enriched_tissues, sep = sep)
class2_long <-
class2 %>%
select(gene2 = gene_col2, enriched_tissues) %>%
mutate(
enriched_tissues = ifelse(is.na(enriched_tissues),
"not enriched",
enriched_tissues),
enriched2 = T
) %>%
separate_rows(enriched_tissues, sep = sep)
tis_ <-
unique(c(class1_long$enriched_tissues,
class2_long$enriched_tissues)) %>%
sort()
gene_orthologs <- homolog_data %>% select(1,2)
overlap_hyper_all <- expand.grid(id1 = tis_,
id2 = tis_,
gene = 1:nrow(gene_orthologs)) %>%
as_tibble() %>%
left_join(gene_orthologs %>%
select(gene1 = gene_col1,
gene2 = gene_col2) %>%
mutate(gene = row_number())) %>%
select(-gene) %>%
left_join(class1_long,
by = c("gene1", "id1" = "enriched_tissues")) %>%
left_join(class2_long,
by = c("gene2", "id2" = "enriched_tissues")) %>%
mutate(
enriched1 = ifelse(is.na(enriched1),
F,
enriched1),
enriched2 = ifelse(is.na(enriched2),
F,
enriched2)
) %>%
group_by(id1, id2, enriched1, enriched2) %>%
count() %>%
group_by(id1, id2) %>%
summarise(
# q is the number of successes
q = sum(n[which(enriched1 & enriched2)]),
# k is the number of tries - i.e. the number of genes that are elevated for either species
k = sum(n[which(enriched1 | enriched2)]),
# m is the number of possible successes - i.e. the number of genes that are elevated for either
m = min(sum(n[which(enriched1)]),
sum(n[which(enriched2)])),
# n is the population size - i.e. the number of genes
n = sum(n) - m
) %>%
mutate(p_value = phyper(q - 1, m, n, k, lower.tail = F)) %>%
mutate(p_value = ifelse(p_value == 0, .Machine$double.xmin, p_value),
adj_pval = p.adjust(p_value, method = "BH")) %>%
rename(rat_id = id1,
human_id = id2)
Joining, by = "gene"`summarise()` has grouped output by 'id1'. You can override using the `.groups` argument.
overlap_hyper_all %>%
write_csv("./data/final_data/rat_human_class_hyper.csv")
plot_order <-
overlap_hyper_all %>%
ungroup() %>%
mutate(rat_id = str_to_sentence(rat_id),
human_id = str_to_sentence(human_id)) %>%
filter(rat_id == human_id) %>%
arrange(adj_pval) %>%
pull(rat_id)
stripped_theme <-
theme(panel.background = element_rect(fill = NA, colour = NA),
plot.background = element_rect(fill = NA, color = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
legend.key = element_rect(colour = NA),
#legend.position = "bottom",
#legend.direction = "horizontal",
legend.key.size= unit(0.3, "cm"),
legend.title = element_text(face="italic"),
axis.line = element_line(colour="black",size=0.5))
overlap_hyper_all %>%
group_by(rat_id, human_id) %>%
mutate(capped_p = min(c(-log10(adj_pval), 20))) %>%
ungroup() %>%
mutate(rat_id = str_to_sentence(rat_id),
human_id = str_to_sentence(human_id)) %>%
mutate(rat_id = factor(rat_id, plot_order),
human_id = factor(human_id, plot_order)) %>%
ggplot(aes(human_id, rat_id, fill = capped_p)) +
geom_tile() +
geom_tile(data = . %>%
filter(adj_pval >= 0.05),
fill = "black") +
scale_fill_viridis(option = "D", direction = 1,
name = "Adjusted p-value") +
stripped_theme +
theme(axis.text.x = element_text(angle = -90, hjust = 0, vjust = 0.2),
legend.position = "top") +
xlab("Human") +
ylab("Rat")

#ggsave("./final_plots/comparison/Overlap hyper heatmap elevated.pdf", width = 6, height = 6)
overlap_hyper_all %>%
filter(human_id != "not enriched" &
rat_id != "not enriched") %>%
ungroup() %>%
mutate(rat_id = str_to_sentence(rat_id),
human_id = str_to_sentence(human_id)) %>%
filter(adj_pval < 0.05) %>%
mutate(rat_id = factor(rat_id, plot_order),
human_id = factor(human_id, plot_order),
adj_pval = case_when(adj_pval < 1e-100 ~ 1e-100,
T ~ adj_pval)) %>%
ggplot(aes(human_id, rat_id, fill = -log10(adj_pval), size = -log10(adj_pval))) +
geom_point(shape = 21,
alpha = 0.8,
stroke = 0.2,
color = "black") +
# scale_color_viridis(option = "E", direction = 1,
# name = "Adjusted p-value") +
scale_fill_gradientn(colors = ggsci::rgb_material(palette = "deep-orange")) +
scale_size_continuous(range = c(1, 6)) +
stripped_theme +
theme(axis.text.x = element_text(angle = -90, hjust = 0, vjust = 0.2),
legend.position = "top",
panel.grid.major = element_line(color = "gray90", size = 0.2)) +
xlab("Human") +
ylab("Rat")
ggsave("./final_plots/comparison/hypergeometric_comparison_tissue.pdf", width = 5, height = 5.5)
Warning: ‘mode(bg)’ differs between new and previous
==> NOT changing ‘bg’

##Figure 7D - Cross species gene annotation alluvial
#classification rat
classification_rat_comp <-
hpa_gene_classification(
data = joined_atlas_comparison %>% filter(species == "rat") %>% select(c(-id,-species,-limma_log1p_tmm)),
expression_col = "tmm",
tissue_col = "tissue",
gene_col = "mutual_id",
enr_fold = 4,
max_group_n = 5,
det_lim = 1
)
rat_comp_tau <- calculate_tau_score(
joined_atlas_comparison %>% filter(species == "rat") %>% select(-id,-species,-limma_log1p_tmm) %>%
spread(tissue, tmm) %>%
mutate_if(is.numeric, function(x) {
log10(x + 1)
}) %>%
column_to_rownames("mutual_id")
)
classification_rat_comp <- classification_rat_comp %>%
left_join(rat_comp_tau, by = c("gene" = "gene")) %>%
mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score)) %>%
mutate(species = "Rat")
#classification human
classification_human_comp <-
hpa_gene_classification(
data = joined_atlas_comparison %>% filter(species == "human") %>% select(c(-id,-species,-limma_log1p_tmm)),
expression_col = "tmm",
tissue_col = "tissue",
gene_col = "mutual_id",
enr_fold = 4,
max_group_n = 5,
det_lim = 1
)
human_comp_tau <- calculate_tau_score(
joined_atlas_comparison %>% filter(species == "human") %>% select(-id,-species,-limma_log1p_tmm) %>%
spread(tissue, tmm) %>%
mutate_if(is.numeric, function(x) {
log10(x + 1)
}) %>%
column_to_rownames("mutual_id")
)
classification_human_comp <- classification_human_comp %>%
left_join(rat_comp_tau, by = c("gene" = "gene")) %>%
mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score)) %>%
mutate(species = "Human")
alluvial_data_comp <- classification_rat_comp %>%
select(gene, spec_category) %>%
rename(Rat = spec_category) %>%
left_join(
classification_human_comp %>%
select(gene, spec_category) %>%
rename(Human = spec_category),
by = "gene"
)
width = 0.1
alluv_1 <-
alluvial_data_comp %>%
mutate(Rat = str_to_sentence(Rat),
Human = str_to_sentence(Human)) %>%
select(Rat,
Human) %>%
mutate(row_n = row_number()) %>%
gather(bar, chunk, -row_n) %>%
mutate(color_vars = 1) %>%
group_by(row_n) %>%
mutate(chunk_color = chunk[match(c("Human",
"Rat")[color_vars], bar)]) %>%
ungroup() %>%
mutate(chunk = factor(chunk, levels = c('Tissue enriched', 'Group enriched',
'Tissue enhanced', 'Low tissue specificity',
'Not detected')),
bar = factor(bar, levels = c("Human",
"Rat"))) %>%
ggplot(aes(x = bar, stratum = chunk, alluvium = row_n,
y = 1)) +
geom_flow(aes(fill = chunk_color),
show.legend = F, width = width,
knot.pos = 1/6) +
geom_stratum(aes(fill = chunk),
show.legend = F, color = NA, width = width) +
scale_x_discrete(expand = c(.1, .1), position = "top") +
scale_fill_manual(values = c(gene_category_pal)) +
theme(axis.text.y = element_text(size = 18, face = "bold"),
axis.text.x = element_blank(),
axis.ticks = element_blank(),
panel.background = element_blank(),
axis.title = element_blank()) +
coord_flip()
# alluv_1
flow_data <-
ggplot_build(alluv_1)$data[[1]] %>%
as_tibble() %>%
{
if ("side" %in% names(.)) {
.
} else{
mutate(.,
side = case_when(flow == "from" ~ "start",
flow == "to" ~ "end"))
}
}
stratum_data <-
ggplot_build(alluv_1)$data[[2]]
flow_data_labels <-
flow_data %>%
as_tibble() %>%
select(x, stratum, group, side, ymin, ymax) %>%
pivot_wider(names_from = side,
values_from = c(x, stratum, ymin, ymax)) %>%
mutate_at(
c(
"x_end",
"ymax_end",
"ymin_end",
"x_start",
"ymax_start",
"ymin_start"
),
as.numeric
) %>%
group_by(stratum_start, stratum_end, x_start, x_end) %>%
summarise(
y_end = (min(ymin_end) + max(ymax_end)) / 2,
y_start = (min(ymin_start) + max(ymax_start)) / 2,
size = max(ymax_start) - min(ymin_start)
)
`summarise()` has grouped output by 'stratum_start', 'stratum_end', 'x_start'. You can override using the `.groups` argument.
alluv_2 <-
alluv_1 +
geom_text(data = flow_data_labels,
aes(x = x_start + width/2,
y = y_start,
label = size),
inherit.aes = F,
size = 3,
angle = -90,
hjust = 1,
vjust = 0.5) +
geom_text(data = flow_data_labels,
aes(x = x_end - width/2,
y = y_end,
label = size),
inherit.aes = F,
size = 3,
angle = -90,
hjust = 0,
vjust = 0.5) +
# Stratum label
geom_text(data = stratum_data %>%
filter(x == 1),
aes(x = x - width/2,
y = y,
label = stratum),
size = 4,
vjust = 1.5,
inherit.aes = F) +
geom_text(data = stratum_data %>%
filter(x == 2),
aes(x = x + width/2,
y = y,
label = stratum),
size = 4,
vjust = -0.5,
inherit.aes = F) +
geom_text(data = stratum_data,
aes(x = x,
y = y,
label = ymax - ymin),
size = 4,
fontface = "bold",
color = "white",
inherit.aes = F)
alluv_2
if (include_many2many == TRUE & include_one2many == TRUE){
comp_alluv_file_name = paste0("./final_plots/comparison/",ortholog_data_from,"_comparison_all_alluvial.pdf")
} else if (include_many2many == FALSE & include_one2many == TRUE){
comp_alluv_file_name = paste0("./final_plots/comparison/",ortholog_data_from,"_comparison_only_on2one2many_alluvial.pdf")
} else if (include_many2many == FALSE & include_one2many == FALSE){
comp_alluv_file_name = paste0("./final_plots/comparison/",ortholog_data_from,"_comparison_only_one2one_alluvial.pdf")}
ggsave(comp_alluv_file_name,width = 8, height = 3)

#Figure S1 - Brain Metadata alluvial plot
#Function adapted from Max Karlsson
multi_alluvial_plot <-
function(data,
vars,
chunk_levels,
pal,
color_by = c(1, 3, 3)) {
selvars = vars
if (!is.null(names(vars))) {
vars = names(vars)
}
alluv_1 <-
data %>%
ungroup() %>%
select(selvars) %>%
ungroup() %>%
mutate(row_n = row_number()) %>%
gather(bar, chunk,-row_n) %>%
left_join(tibble(bar = vars,
color_vars = color_by),
by = "bar") %>%
group_by(row_n) %>%
mutate(chunk_color = chunk[match(vars[color_vars], bar)]) %>%
ungroup() %>%
mutate(chunk = factor(chunk, levels = chunk_levels),
bar = factor(bar, levels = vars)) %>%
ggplot(aes(
x = bar,
stratum = chunk,
alluvium = row_n,
y = 1
)) +
geom_flow(aes(fill = chunk_color),
show.legend = F) +
geom_stratum(aes(fill = chunk),
show.legend = F, color = NA) +
scale_x_discrete(expand = c(.1, .1), position = "top") +
scale_fill_manual(values = pal) +
theme(
axis.text.x = element_text(size = 18, face = "bold"),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
panel.background = element_blank(),
axis.title = element_blank()
)
flow_data <-
ggplot_build(alluv_1)$data[[1]] %>%
as_tibble() %>%
{
if ("side" %in% names(.)) {
.
} else{
mutate(.,
side = case_when(flow == "from" ~ "start",
flow == "to" ~ "end"))
}
}
stratum_data <-
ggplot_build(alluv_1)$data[[2]]
flow_data_labels <-
flow_data %>%
as_tibble() %>%
select(x, stratum, group, side, ymin, ymax) %>%
pivot_wider(names_from = side,
values_from = c(x, stratum, ymin, ymax)) %>%
mutate_at(
c(
"x_end",
"ymax_end",
"ymin_end",
"x_start",
"ymax_start",
"ymin_start"
),
as.numeric
) %>%
group_by(stratum_start, stratum_end, x_start, x_end) %>%
summarise(
y_end = (min(ymin_end) + max(ymax_end)) / 2,
y_start = (min(ymin_start) + max(ymax_start)) / 2,
size = max(ymax_start) - min(ymin_start)
)
alluv_1 <-
alluv_1 +
# geom_text(
# data = flow_data_labels,
# aes(x = x_start + 1 / 6,
# y = y_start,
# label = size),
# inherit.aes = F,
# size = 3,
# hjust = 0
# ) +
# geom_text(
# data = flow_data_labels,
# aes(x = x_end - 1 / 6,
# y = y_end,
# label = size),
# inherit.aes = F,
# size = 3,
# hjust = 1
# ) +
#
# Stratum label
geom_text(
data = stratum_data,
aes(
x = x,
y = y,
label = paste(stratum#,
# paste("[", ymax - ymin, "]", sep = "")
)
),
size = 4,
inherit.aes = F
)
alluv_1
}
metadata_b <- metadata %>% filter(organ_name =="Brain")
t_names <- metadata_b$tissue_name %>% unique()
r_names <- metadata_b$region_tissue_name %>% unique()
c_names <- metadata_b$consensus_tissue_name %>% unique()
o_names <- metadata_b$organ_name %>% unique()
t_colors <- metadata_b %>% select(tissue_name, tissue_color) %>% unique() %>% rename (chunk = tissue_name, color = tissue_color)
r_colors <- metadata_b %>% select(region_tissue_name, region_tissue_color) %>% unique() %>% rename (chunk = region_tissue_name, color = region_tissue_color)
c_colors <- metadata_b %>% select(consensus_tissue_name, consensus_tissue_color) %>% unique() %>% rename (chunk = consensus_tissue_name, color = consensus_tissue_color)
o_colors <- metadata_b %>% select(organ_name, organ_color) %>% unique() %>% rename (chunk = organ_name, color = organ_color)
bind_colors <- bind_rows(t_colors, r_colors, o_colors) %>% unique() %>% arrange(chunk) %>% mutate(row_n = row_number())
pal <- bind_colors$color
pal <- setNames(pal, bind_colors$chunk)
data = metadata_b
vars = c("tissue_name", "region_tissue_name", "organ_name")
chunk_levels = c(t_names, r_names, o_names) %>% unique()
color_by = c(1, 3, 3)
multi_alluvial_plot(data = metadata_b, vars = vars, chunk_levels = chunk_levels, pal = pal, color_by = c(1, 3, 3))
`summarise()` has grouped output by 'stratum_start', 'stratum_end', 'x_start'. You can override using the `.groups` argument.
ggsave("final_plots/alluvial/brain-tco-1_p.pdf", height = 7, width = 10)

#Figure S2 - Normalisation comparison TPM vs nTPM (TMM)
tpm_sample <-read_csv("./data/final_data/curated_pTPM_rattus_norvegicus_v103.csv")
Rows: 22245 Columns: 353── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): target_id
dbl (352): ventral.forebrain_male.3, ventral.forebrain_male.2, ventral.forebrain_female.3, ventral.forebrain_female...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
sample_subset_ID <- metadata[match(unique(metadata$consensus_tissue_name), metadata$consensus_tissue_name),] %>% select(ID)
sample_subset_tmm <- tmm_sample %>% select(target_id, sample_subset_ID$ID) %>% gather(sample, tmm, -1)
sample_subset_tpm <- tpm_sample %>% select(target_id, sample_subset_ID$ID) %>% gather(sample, tpm, -1)
# plot_data <- left_join(sample_subset_tmm, sample_subset_tpm, by = c("target_id", "sample")) %>%
# mutate(log1p_tmm = log10(tmm + 1), log1p_tpm = log10(tpm +1)) %>% select(-tmm, -tpm) %>%
# gather(expression_type, expression, -1, -2) %>%
# left_join(metadata %>% select(ID, tissue_name, consensus_tissue_name), by = c("sample" = "ID"))
plot_data <- left_join(sample_subset_tmm, sample_subset_tpm, by = c("target_id", "sample")) %>%
gather(expression_type, expression, -1, -2) %>% mutate_if(is.numeric, function(x) {
log10(x + 1)
}) %>%
left_join(metadata %>% select(ID, tissue_name, consensus_tissue_name), by = c("sample" = "ID"))
pal_tbl <- metadata %>% select(consensus_tissue_name, consensus_tissue_color) %>% distinct()
pal <- pal_tbl %>% pull(consensus_tissue_color)
pal <- setNames(pal, pal_tbl %>% pull(consensus_tissue_name))
ggplot(data = plot_data, aes(x = expression, y =sample, fill = consensus_tissue_name)) +
geom_boxplot(draw_quantiles = 0.5, outlier.size = 0.5, outlier.alpha = 0.3)+
facet_wrap(~expression_type)+
scale_fill_manual(values = pal) +
theme(legend.position = "none",
axis.title = element_blank())
Warning: Ignoring unknown parameters: `draw_quantiles`
ggsave("./final_plots/tpm_tmm_comp_boxplot.pdf", width=7, height = 12)

#Figure S3 - Spearman heatmap (tissye type level)
##Spearman's roh heatmap at tissue type level
if(file.exists("./data/final_data/spearman_corr_tissues.csv")) {
tissue_tmm_spearman <- read_csv("./data/final_data/spearman_corr_tissues.csv")
} else {
tissue_tmm_spearman <- tmm_tissue %>%
spread(tissue_name, tmm) %>%
column_to_rownames("target_id") %>%
cor(method = "spearman", use = "pairwise.complete.obs") %>%
as.data.frame() %>%
as_tibble(rownames = "tissue_name")
write_csv(as.data.frame(tissue_tmm_spearman) %>% as_tibble(rownames = "tissue_name"),"./data/final_data/spearman_corr_tissues.csv")
}
Rows: 100 Columns: 101── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): tissue_name
dbl (100): abdominal adipose tissue, adrenal gland, amygdala, aorta, artery, bone marrow, breast, bronchus, brown a...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
tissue_tmm_spearman %>%
column_to_rownames("tissue_name") %>%
pheatmap(
# clustering_method = "ward.D2",
cellheight = 8,
cellwidth = 8,
border_color = NA,
color = viridis::inferno(20, direction = -1),
show_rownames = FALSE,
) %>%
as.ggplot()

ggsave("./final_plots/data_presentation/spearman_corr_tissue.pdf", height = 20, width = 20)

NA
NA
#Figure S4 - Comparison Pheatmap
if(file.exists("./data/final_data/spearman_corr_tissues.csv")) {
tissue_tmm_spearman <- read_csv("./data/final_data/spearman_corr_tissues.csv")
} else {
tissue_tmm_spearman <- tmm_tissue %>%
spread(tissue_name, tmm) %>%
column_to_rownames("target_id") %>%
cor(method = "spearman", use = "pairwise.complete.obs") %>%
as.data.frame() %>%
as_tibble(rownames = "tissue_name")
write_csv(as.data.frame(tissue_tmm_spearman) %>% as_tibble(rownames = "tissue_name"),"./data/final_data/spearman_corr_tissues.csv")
}
Rows: 100 Columns: 101── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): tissue_name
dbl (100): abdominal adipose tissue, adrenal gland, amygdala, aorta, artery, bone marrow, breast, bronchus, brown a...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
joined_atlas_comparison_pheat_data <- joined_atlas_comparison %>%
select(mutual_id, id,limma_log1p_tmm ) %>%
spread(id, limma_log1p_tmm) %>%
column_to_rownames("mutual_id") %>%
cor(method = "spearman", use = "pairwise.complete.obs") %>%
as.data.frame() %>%
as_tibble(rownames = "tissue_name")
joined_atlas_comparison_pheat_data %>%
column_to_rownames("tissue_name") %>%
pheatmap(
# clustering_method = "ward.D2",
cellheight = 8,
cellwidth = 8,
border_color = NA,
color = viridis::inferno(20, direction = -1),
show_rownames = FALSE,
) %>%
as.ggplot()

ggsave("./final_plots/comparison/pheatmap.pdf", width = 20, height = 20)

sessionInfo()
R version 4.2.1 (2022-06-23)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Ventura 13.1
Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggsci_2.9 geomtextpath_0.1.1 plotly_4.10.1 influential_2.2.6 magrittr_2.0.3
[6] viridis_0.6.2 viridisLite_0.4.1 igraph_1.3.5 patchwork_1.1.2 ggraph_2.1.0
[11] ggrepel_0.9.2 ggplotify_0.1.0 pheatmap_1.0.12 uwot_0.1.14.9000 Matrix_1.5-3
[16] ggdendro_0.1.23 ggalluvial_0.12.3 forcats_0.5.2 stringr_1.5.0 dplyr_1.0.10
[21] purrr_0.3.5 readr_2.1.3 tidyr_1.2.1 tibble_3.1.8 ggplot2_3.4.0
[26] tidyverse_1.3.2 biomaRt_2.52.0 pcaMethods_1.88.0 Biobase_2.56.0 BiocGenerics_0.42.0
loaded via a namespace (and not attached):
[1] readxl_1.4.1 backports_1.4.1 BiocFileCache_2.4.0 systemfonts_1.0.4
[5] plyr_1.8.8 lazyeval_0.2.2 sp_1.5-1 splines_4.2.1
[9] NOISeq_2.40.0 GenomeInfoDb_1.32.4 digest_0.6.31 yulab.utils_0.0.5
[13] htmltools_0.5.4 fansi_1.0.3 memoise_2.0.1 googlesheets4_1.0.1
[17] cluster_2.1.4 tzdb_0.3.0 limma_3.52.4 Biostrings_2.64.1
[21] graphlayouts_0.8.4 modelr_0.1.10 vroom_1.6.0 timechange_0.1.1
[25] prettyunits_1.1.1 colorspace_2.0-3 blob_1.2.3 rvest_1.0.3
[29] rappdirs_0.3.3 textshaping_0.3.6 haven_2.5.1 xfun_0.35
[33] crayon_1.5.2 RCurl_1.98-1.9 jsonlite_1.8.4 glue_1.6.2
[37] polyclip_1.10-4 gtable_0.3.1 gargle_1.2.1 zlibbioc_1.42.0
[41] XVector_0.36.0 kernlab_0.9-31 prabclus_2.3-2 DEoptimR_1.0-11
[45] scales_1.2.1 DBI_1.1.3 Rcpp_1.0.9 progress_1.2.2
[49] gridGraphics_0.5-1 bit_4.0.5 mclust_6.0.0 stats4_4.2.1
[53] htmlwidgets_1.5.4 httr_1.4.4 FNN_1.1.3.1 RColorBrewer_1.1-3
[57] fpc_2.2-9 modeltools_0.2-23 ellipsis_0.3.2 pkgconfig_2.0.3
[61] XML_3.99-0.13 flexmix_2.3-18 farver_2.1.1 sass_0.4.4
[65] nnet_7.3-18 dbplyr_2.2.1 utf8_1.2.2 labeling_0.4.2
[69] tidyselect_1.2.0 rlang_1.0.6 AnnotationDbi_1.58.0 munsell_0.5.0
[73] cellranger_1.1.0 tools_4.2.1 cachem_1.0.6 cli_3.4.1
[77] generics_0.1.3 RSQLite_2.2.19 broom_1.0.1 evaluate_0.19
[81] fastmap_1.1.0 ragg_1.2.4 yaml_2.3.6 knitr_1.41
[85] bit64_4.0.5 fs_1.5.2 tidygraph_1.2.2 robustbase_0.95-0
[89] KEGGREST_1.36.3 xml2_1.3.3 compiler_4.2.1 rstudioapi_0.14
[93] filelock_1.0.2 curl_4.3.3 png_0.1-8 reprex_2.0.2
[97] tweenr_2.0.2 bslib_0.4.1 stringi_1.7.8 lattice_0.20-45
[101] vctrs_0.5.1 pillar_1.8.1 lifecycle_1.0.3 jquerylib_0.1.4
[105] data.table_1.14.6 irlba_2.3.5.1 bitops_1.0-7 R6_2.5.1
[109] gridExtra_2.3 IRanges_2.30.1 MASS_7.3-58.1 assertthat_0.2.1
[113] withr_2.5.0 S4Vectors_0.34.0 GenomeInfoDbData_1.2.8 diptest_0.76-0
[117] parallel_4.2.1 hms_1.1.2 grid_4.2.1 class_7.3-20
[121] rmarkdown_2.18 googledrive_2.0.0 ggforce_0.4.1 lubridate_1.9.0
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ2FsbHV2aWFsKQpsaWJyYXJ5KGJpb21hUnQpCmxpYnJhcnkoZ2dkZW5kcm8pCmxpYnJhcnkocGNhTWV0aG9kcykKbGlicmFyeSh1d290KQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KGdncGxvdGlmeSkKbGlicmFyeSAoZ2dyZXBlbCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkocGF0Y2h3b3JrKQoKCgpzZWxlY3QgPC0gZHBseXI6OnNlbGVjdApgYGAKCmBgYHtyfQp0bW1fc2FtcGxlIDwtIHJlYWRfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9jdXJhdGVkX3BUTU1fcmF0dHVzX25vcnZlZ2ljdXNfdjEwMy5jc3YiKQptZXRhZGF0YSA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvY3VyYXRlZF9tZXRhZGF0YS5jc3YiKQphbGwoc29ydChjb2xuYW1lcyh0bW1fc2FtcGxlWy0xXSkpID09IHNvcnQobWV0YWRhdGEkSUQpKQoKYGBgCgojR2VuZXJhdGUgaGllcmFyY2hpY2FsIGRhdGEKYGBge3J9CgoKI3Nsb3csIGJ1dCB1bmRlcnN0YW5hYmxlCnRtbV90aXNzdWUgPC0gdG1tX3NhbXBsZSAlPiUgCiAgZ2F0aGVyKHNhbXBsZSwgdG1tLCAtMSkgJT4lIAogIGxlZnRfam9pbihtZXRhZGF0YSAlPiUgc2VsZWN0KElELHRpc3N1ZV9uYW1lKSwgYnkgPSBjKCJzYW1wbGUiID0gIklEIikpICU+JSAKICBncm91cF9ieSh0YXJnZXRfaWQsIHRpc3N1ZV9uYW1lKSAlPiUgCiAgc3VtbWFyaXplKHRtbSA9IG1lYW4odG1tKSkgJT4lIAogIHVuZ3JvdXAoKQoKd3JpdGVfY3N2KHRtbV90aXNzdWUsIGZpbGU9Ii4vZGF0YS9maW5hbF9kYXRhL2ZpbmFsX3RtbV90aXNzdWVfbmFtZS5jc3YiKQoKdG1tX3JlZ2lvbl90aXNzdWUgPC0gdG1tX3Rpc3N1ZSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhICU+JSBzZWxlY3QodGlzc3VlX25hbWUscmVnaW9uX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJ0aXNzdWVfbmFtZSIgPSAidGlzc3VlX25hbWUiKSkgJT4lIAogIGdyb3VwX2J5KHRhcmdldF9pZCwgcmVnaW9uX3Rpc3N1ZV9uYW1lKSAlPiUgCiAgc3VtbWFyaXplKHRtbSA9IG1heCh0bW0pKSAlPiUgCiAgdW5ncm91cCgpCgp3cml0ZV9jc3YodG1tX3JlZ2lvbl90aXNzdWUsIGZpbGU9Ii4vZGF0YS9maW5hbF9kYXRhL2ZpbmFsX3RtbV9yZWdpb25fdGlzc3VlX25hbWUuY3N2IikKCnRtbV9jb25zZW5zdXNfdGlzc3VlIDwtIHRtbV9yZWdpb25fdGlzc3VlICU+JSAKICBsZWZ0X2pvaW4obWV0YWRhdGEgJT4lIHNlbGVjdChyZWdpb25fdGlzc3VlX25hbWUsY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJyZWdpb25fdGlzc3VlX25hbWUiID0gInJlZ2lvbl90aXNzdWVfbmFtZSIpKSAlPiUgCiAgZ3JvdXBfYnkodGFyZ2V0X2lkLCBjb25zZW5zdXNfdGlzc3VlX25hbWUpICU+JSAKICBzdW1tYXJpemUodG1tID0gbWF4KHRtbSkpICU+JSAKICB1bmdyb3VwKCkKCndyaXRlX2Nzdih0bW1fY29uc2Vuc3VzX3Rpc3N1ZSwgZmlsZT0iLi9kYXRhL2ZpbmFsX2RhdGEvZmluYWxfdG1tX2NvbnNlbnN1c19uYW1lLmNzdiIpCgojY2hlY2sKYWxsKG1ldGFkYXRhJHRpc3N1ZV9uYW1lICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpID09IHRtbV90aXNzdWUkdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpICU+JSBzb3J0KCkpCmFsbChtZXRhZGF0YSRyZWdpb25fdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpICU+JSBzb3J0KCkgPT0gdG1tX3JlZ2lvbl90aXNzdWUkcmVnaW9uX3Rpc3N1ZV9uYW1lICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpKQphbGwobWV0YWRhdGEkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpID09IHRtbV9jb25zZW5zdXNfdGlzc3VlJGNvbnNlbnN1c190aXNzdWVfbmFtZSAlPiUgdW5pcXVlKCkgJT4lIHNvcnQoKSkKYGBgCgpgYGB7cn0KIyAjIGZhc3RlciwgYnV0IGxlc3MgdW5kZXJzdGFuZGFibGUKIyB1bmlxdWVfdGlzc3VlIDwtIHNlbGVjdChtZXRhZGF0YSx0aXNzdWVfbmFtZSkgJT4lCiMgICBkaXN0aW5jdCgpCiMgdG1tX2J5X3Rpc3N1ZSA8LSB0aWJibGUodGFyZ2V0X2lkID0gdG1tX3NhbXBsZSR0YXJnZXRfaWQpCiMgZm9yIChpIGluIHVuaXF1ZV90aXNzdWUkdGlzc3VlX25hbWUpIHsKIyAgIGN1cmVudF90aXNzdWVfbWV0YSA9IG1ldGFkYXRhW21ldGFkYXRhJHRpc3N1ZV9uYW1lID09IGksXQojICAgY3VycmVudF90bW0gPSBzZWxlY3QodG1tX3NhbXBsZSxjKHRhcmdldF9pZCwgY3VyZW50X3Rpc3N1ZV9tZXRhJElEKSkKIyAgIG1lYW5zIDwtIGN1cnJlbnRfdG1tICU+JSBzZWxlY3QoY3VyZW50X3Rpc3N1ZV9tZXRhJElEKSAlPiUgcm93TWVhbnMoKQojICAgdG1tX2J5X3Rpc3N1ZVt0b1N0cmluZyhpKV0gPC0gbWVhbnMKIyB9CiMgI3RtbV9ieV90aXNzdWUgPC0gdG1tX2J5X3Rpc3N1ZSAlPiUgZ2F0aGVyKHRpc3N1ZV9uYW1lLCB0bW0sIC0xKQojICN3cml0ZS5jc3YodG1tX2J5X3Rpc3N1ZSwgZmlsZT0idG1tX3Rpc3N1ZV9uYW1lLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHVuaXF1ZV9yZWdpb24gPC0gc2VsZWN0KG1ldGFkYXRhLHJlZ2lvbl90aXNzdWVfbmFtZSkgJT4lCiMgICBkaXN0aW5jdCgpCiMgdG1tX2J5X3JlZ2lvbiA8LSB0aWJibGUodGFyZ2V0X2lkID0gdG1tX3NhbXBsZSR0YXJnZXRfaWQpCiMgZm9yIChpIGluIHVuaXF1ZV9yZWdpb24kcmVnaW9uX3Rpc3N1ZV9uYW1lKSB7CiMgICBjdXJyZW50X3JlZ2lvbl9tZXRhID0gbWV0YWRhdGFbbWV0YWRhdGEkcmVnaW9uX3Rpc3N1ZV9uYW1lID09IGksXQojICAgY3VycmVudF90bW0gPSBzZWxlY3QodG1tX2J5X3Rpc3N1ZSwgYyh0YXJnZXRfaWQsIHVuaXF1ZShjdXJyZW50X3JlZ2lvbl9tZXRhJHRpc3N1ZV9uYW1lKSkpCiMgICBtYXggPC0gc2VsZWN0KGN1cnJlbnRfdG1tLC10YXJnZXRfaWQpICU+JSBhcHBseSgxLG1heCkKIyAgIHRtbV9ieV9yZWdpb25bdG9TdHJpbmcoaSldIDwtIG1heAojIH0KIyAKIyAjIHdyaXRlLmNzdih0bW1fYnlfcmVnaW9uLCBmaWxlPSJ0bW1fcmVnaW9uX25hbWUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCiMgCiMgdW5pcXVlX2NvbnNlbnN1cyA8LSBzZWxlY3QobWV0YWRhdGEsY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSAlPiUKIyAgIGRpc3RpbmN0KCkKIyB0bW1fYnlfY29uc2Vuc3VzIDwtIHRpYmJsZSh0YXJnZXRfaWQgPSB0bW1fc2FtcGxlJHRhcmdldF9pZCkKIyBmb3IgKGkgaW4gdW5pcXVlX2NvbnNlbnN1cyRjb25zZW5zdXNfdGlzc3VlX25hbWUpIHsKIyAgIGN1cnJlbnRfY29uc2Vuc3VzX21ldGEgPSBtZXRhZGF0YVttZXRhZGF0YSRjb25zZW5zdXNfdGlzc3VlX25hbWUgPT0gaSxdCiMgICBjdXJyZW50X3RtbSA9IHNlbGVjdCh0bW1fYnlfcmVnaW9uLCBjKHRhcmdldF9pZCwgdW5pcXVlKGN1cnJlbnRfY29uc2Vuc3VzX21ldGEkcmVnaW9uX3Rpc3N1ZV9uYW1lKSkpCiMgICBtYXggPC0gc2VsZWN0KGN1cnJlbnRfdG1tLC10YXJnZXRfaWQpICU+JSBhcHBseSgxLG1heCkKIyAgIHRtX2J5X2NvbnNlbnN1c1t0b1N0cmluZyhpKV0gPC0gbWF4CiMgfQoKI3dyaXRlLmNzdih0bW1fYnlfY29uc2Vuc3VzLCBmaWxlPSJ0bW1fY29uc2Vuc3VzX25hbWUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKI0ZpZ3VyZSAxCiMjRmlndXJlIDFBIC0gSGllcmFyY2h5IE92ZXJ2aWV3CmBgYHtyfQp0aXNzdWVfY29sb3JzX3BhbGV0dGVfZnVsbCA8LSByYmluZCgKICBtZXRhZGF0YSAlPiUgc2VsZWN0KG5hbWUgPSB0aXNzdWVfbmFtZSwgY29sb3IgPSB0aXNzdWVfY29sb3IpLCAKICBtZXRhZGF0YSAlPiUgc2VsZWN0KG5hbWUgPSBjb25zZW5zdXNfdGlzc3VlX25hbWUsIGNvbG9yID0gY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvciksCiAgbWV0YWRhdGEgJT4lIHNlbGVjdChuYW1lID0gb3JnYW5fbmFtZSwgY29sb3IgPSBvcmdhbl9jb2xvcikKKSAlPiUgZGlzdGluY3QoKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBzdHJfdG9fc2VudGVuY2UobmFtZSkpICU+JSBhcnJhbmdlKG5hbWUpCgpwYWwgPC0gdGlzc3VlX2NvbG9yc19wYWxldHRlX2Z1bGwkY29sb3IKcGFsIDwtIHNldF9uYW1lcyhwYWwsdGlzc3VlX2NvbG9yc19wYWxldHRlX2Z1bGwkbmFtZSApCgoKcGxvdF9kYXRhMSA8LSAKICBtZXRhZGF0YSAlPiUKICBzZWxlY3QodGlzc3VlX25hbWUsIHJlZ2lvbl90aXNzdWVfbmFtZSwgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lLCBvcmdhbl9uYW1lKSAlPiUgIywKICAgICAgICAgI3Rpc3N1ZV9jb2xvciwgcmVnaW9uX3Rpc3N1ZV9jb2xvciwgY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvciwgb3JnYW5fY29sb3IpICU+JSAKICBtdXRhdGUodGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlX25hbWUpLCByZWdpb25fdGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UocmVnaW9uX3Rpc3N1ZV9uYW1lKSwgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lID0gc3RyX3RvX3NlbnRlbmNlKGNvbnNlbnN1c190aXNzdWVfbmFtZSksIG9yZ2FuX25hbWUgPSBzdHJfdG9fc2VudGVuY2UoIG9yZ2FuX25hbWUpKSAlPiUgICB1bmlxdWUoKSAlPiUgCiAgbXV0YXRlKG9yZ2FuX25hbWUgPSBmYWN0b3IoY2FzZV93aGVuKG9yZ2FuX25hbWUgPT0gIk1hbGUgcmVwcm9kdWN0aXZlIHN5c3RlbSIgfiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWFsZSB0aXNzdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5fbmFtZSA9PSAiQnJlYXN0IGFuZCBmZW1hbGUgcmVwcm9kdWN0aXZlIHN5c3RlbSIgfgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGZW1hbGUgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuX25hbWUgPT0gIkFkaXBvc2UgJiBzb2Z0IHRpc3N1ZSIgfiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29ubmVjdGl2ZSAmIHNvZnQgdGlzc3VlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5fbmFtZSA9PSAiQm9uZSBtYXJyb3cgJiBpbW11bmUgc3lzdGVtIiB+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJvbmUgbWFycm93ICYgbHltcGhvaWQgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFQgfiBvcmdhbl9uYW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJCcmFpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXllIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvY3JpbmUgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVzcGlyYXRvcnkgc3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcm94aW1hbCBkaWdlc3RpdmUgdHJhY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdhc3Ryb2ludGVzdGluYWwgdHJhY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxpdmVyICYgZ2FsbGJsYWRkZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktpZG5leSAmIHVyaW5hcnkgYmxhZGRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGFuY3JlYXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1hbGUgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmVtYWxlIHRpc3N1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk11c2NsZSB0aXNzdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb25uZWN0aXZlICYgc29mdCB0aXNzdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNraW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJvbmUgbWFycm93ICYgbHltcGhvaWQgdGlzc3VlcyIpKSkKCnBsb3RfZGF0YTEgPC0gcGxvdF9kYXRhMSAlPiUgCiAgYXJyYW5nZShvcmdhbl9uYW1lLAogICAgICAgICAgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lLAogICAgICAgICAgcmVnaW9uX3Rpc3N1ZV9uYW1lLAogICAgICAgICAgdGlzc3VlX25hbWUpICU+JSAKICBtdXRhdGUocGxvdF9vcmRlciA9IHJvd19udW1iZXIoKSkKCnBsb3RfZGF0YTIgPC0gCiAgcGxvdF9kYXRhMSAlPiUKICBzZWxlY3QoLXJlZ2lvbl90aXNzdWVfbmFtZSkgJT4lCiAgZ2F0aGVyKGNvbHVtbiwgbGFiZWwsIC1wbG90X29yZGVyKSAlPiUKICBncm91cF9ieShsYWJlbCwgY29sdW1uKSAlPiUgCiAgc3VtbWFyaXNlKHBsb3Rfb3JkZXIgPSBtZWFuKHBsb3Rfb3JkZXIpKSAgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBsYWJlbCwKICAgICAgICAgY29sdW1uID0gZmFjdG9yKGNvbHVtbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGMoIm9yZ2FuX25hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbnNlbnN1c190aXNzdWVfbmFtZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAidGlzc3VlX25hbWUiKSkpCgpwbG90X2RhdGEzIDwtIAogIHBsb3RfZGF0YTEgJT4lIAogIGdyb3VwX2J5KGNvbnNlbnN1c190aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShsZWZ0X3BvcyA9IG1lYW4ocGxvdF9vcmRlcikpCgpwbG90X2RhdGE0IDwtIAogIHBsb3RfZGF0YTIgJT4lIAogIGxlZnRfam9pbih0aXNzdWVfY29sb3JzX3BhbGV0dGVfZnVsbCwKICAgICAgICAgICAgYnkgPSBjKCJsYWJlbCIgPSAibmFtZSIpKSAlPiUgCiAgZ3JvdXBfYnkoY29sdW1uLCBsYWJlbCkgJT4lIAogIHN1bW1hcmlzZShtaW55ID0gbWluKHBsb3Rfb3JkZXIpIC0gMC41LAogICAgICAgICAgICBtYXh5ID0gbWF4KHBsb3Rfb3JkZXIpICsgMC41KQoKCmdncGxvdCgpICsKICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQsIAogICAgICAgICAgIGFlcyh4bWluID0gY29sdW1uLCB4bWF4ID0gY29sdW1uLCB5bWluID0gbWlueSwgeW1heCA9ICBtYXh5LCBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYKICAgICAgICAgKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQgJT4lIGZpbHRlciAoY29sdW1uID09ICJ0aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMywgeG1heCA9IDQsIHltaW4gPSBtaW55LCB5bWF4ID0gIG1heHksIGZpbGwgPSBsYWJlbCksIAogICAgICAgICAgIHNob3cubGVnZW5kID0gRgogICAgICAgICApICsKICAgIGdlb21fcmVjdChkYXRhID0gcGxvdF9kYXRhNCAlPiUgZmlsdGVyIChjb2x1bW4gPT0gImNvbnNlbnN1c190aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMSwgeG1heCA9IDIsIHltaW4gPSBtaW55LCB5bWF4ID0gIG1heHksIGZpbGwgPSBsYWJlbCksIAogICAgICAgICAgIHNob3cubGVnZW5kID0gRiwgd2lkdGggPSAxMAogICAgICAgICApICsKICBnZW9tX3NlZ21lbnQoCiAgICBkYXRhID0gcGxvdF9kYXRhMywKICAgIGFlcygKICAgICAgeCA9ICJjb25zZW5zdXNfdGlzc3VlX25hbWUiLAogICAgICB4ZW5kID0gInRpc3N1ZV9uYW1lIiwKICAgICAgeSA9IGxlZnRfcG9zLAogICAgICB5ZW5kID0gcGxvdF9vcmRlciwKICAgICAgY29sb3IgPSB0aXNzdWVfbmFtZQogICAgKSwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGFscGhhID0gMC41LAogICAgc2l6ZSA9IDIKICApKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBwbG90X2RhdGEyICU+JSBmaWx0ZXIoY29sdW1uID09ICJjb25zZW5zdXNfdGlzc3VlX25hbWUiKSwKICAgIGFlcyh4ID0gY29sdW1uLCB5ID0gcGxvdF9vcmRlciwgbGFiZWwgPSBsYWJlbCksCiAgICBoanVzdCA9IDEsCiAgICBzaXplID0gMiAqIDUgLyA2LAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIikKICApICsKICBnZW9tX3RleHQoCiAgICBkYXRhID0gcGxvdF9kYXRhMiAlPiUgZmlsdGVyKGNvbHVtbiA9PSAidGlzc3VlX25hbWUiKSwKICAgIGFlcyh4ID0gY29sdW1uLCB5ID0gcGxvdF9vcmRlciwgbGFiZWwgPSBsYWJlbCksCiAgICBoanVzdCA9IDAsCiAgICBzaXplID0gMiAqIDUgLyA2LAogICAgc2hvdy5sZWdlbmQgPSBGLAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIikKICApICsKICBnZW9tX2xhYmVsKAogICAgZGF0YSA9IHBsb3RfZGF0YTIgJT4lCiAgICAgIGZpbHRlcihjb2x1bW4gPT0gIm9yZ2FuX25hbWUiKSwKICAgICNhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgYWVzKHggPSBjb2x1bW4sIHkgPSBwbG90X29yZGVyLCBsYWJlbCA9IGdzdWIoIiAiLCAiXG4iLCBsYWJlbCkpLAogICAgc2hvdy5sZWdlbmQgPSBGLAogICAgbGFiZWwuc2l6ZSA9IDAsCiAgICBoanVzdCA9IDEsCiAgICBsaW5laGVpZ2h0ID0gMC43LAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIiksCiAgICBzaXplID0gMiAqIDUgLyA2CiAgKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzICA9IGMoJ09yZ2FuIHN5c3RlbScsICJHcm91cGVkIHRpc3N1ZSIsICJUaXNzdWUgdHlwZSIpLHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgpKQpnZ3NhdmUoImZpbmFsX3Bsb3RzL3Rpc3N1ZXMucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSA0KQoKYGBgCgojI0ZpZ3VyZSAxQiAtIFdhcmQncyBSZXRpbmEtRGVuZHJvZ3JhbW0KYGBge3J9CgojI0Z1bmN0aW5vIGJhc2VkIG9uIE1heCBLYXJsc3NvbidzIHJldGlub2dyYW1tCmNpcmN1bGFyX2RlbmRyb2dyYW1fcmV0aW5hc3R5bGVfMiA8LQogIGZ1bmN0aW9uKGNsdXN0LCBjb2xvcl9tYXBwaW5nLCBsYWJlbF9jb2wsIGNvbG9yX2NvbCwgCiAgICAgICAgICAgc2NhbGVfZXhwYW5zaW9uID0gYygwLjI1LCAwLjI1KSwgdGV4dF9zaXplID0gMywgd2lkdGhfcmFuZ2UgPSBjKDEuNSwgNiksIAogICAgICAgICAgIGFyY19zdHJlbmd0aCA9IDAuOCwgZGVmYXVsdF9jb2xvciA9ICJncmF5ODAiKSB7CiAgICByZXF1aXJlKGdncmFwaCkKICAgIHJlcXVpcmUoaWdyYXBoKQogICAgcmVxdWlyZSh2aXJpZGlzKQogICAgcmVxdWlyZSh0aWR5dmVyc2UpCiAgICByZXF1aXJlKG1hZ3JpdHRyKQogICAgCiAgICBkZW5kcm9ncmFtIDwtCiAgICAgIGNsdXN0ICU+JQogICAgICBhcy5kZW5kcm9ncmFtKCkKICAgIAogICAgCiAgICAKICAgIGcgPC0KICAgICAgZ2dyYXBoKGRlbmRyb2dyYW0sIGxheW91dCA9ICdkZW5kcm9ncmFtJywgY2lyY3VsYXIgPSBUKQogICAgIyAKICAgICMgZyArCiAgICAjICAgZ2VvbV9lZGdlX2ZhbihkYXRhID0gZWRnZV9kYXRhICU+JQogICAgIyAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGhnaGwgPSBlZGdlLmlkID09IDk5KSwKICAgICMgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBlZGdlX2lkLCBjb2xvciA9IGFzLmZhY3RvcihyYW5rX3JhZGl1cykpLAogICAgIyAgICAgICAgICAgICAgICAgIHdpZHRoID00KSArCiAgICAjICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpKQogICAgCiAgICBlZGdlX2RhdGEgPC0gCiAgICAgIGdldF9lZGdlcygpKGckZGF0YSkgJT4lCiAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICBsZWZ0X2pvaW4oY29sb3JfbWFwcGluZyAlPiUKICAgICAgICAgICAgICAgICAgc2VsZWN0KGxhYmVsID0gbGFiZWxfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvcl9jb2wpLAogICAgICAgICAgICAgICAgYnkgPSBjKCJub2RlMi5sYWJlbCIgPSAibGFiZWwiKSkgJT4lCiAgICAgIG11dGF0ZShyYWRpdXMgPSB4ZW5kXjIgKyB5ZW5kXjIsCiAgICAgICAgICAgICBlZGdlLmlkID0gYXMuY2hhcmFjdGVyKGVkZ2UuaWQpKSAlPiUKICAgICAgYXJyYW5nZSgtcmFkaXVzKSAlPiUKICAgICAgbXV0YXRlKGVkZ2VfaWQgPSBhcy5jaGFyYWN0ZXIocm93X251bWJlcigpKSwKICAgICAgICAgICAgIHJhbmtfcmFkaXVzID0gdW5jbGFzcyhmYWN0b3IoLXJhZGl1cykpLAogICAgICAgICAgICAgeF9tID0gcm91bmQoeCwgMTApLAogICAgICAgICAgICAgeV9tID0gcm91bmQoeSwgMTApLAogICAgICAgICAgICAgeGVuZF9tID0gcm91bmQoeGVuZCwgMTApLAogICAgICAgICAgICAgeWVuZF9tID0gcm91bmQoeWVuZCwgMTApKSAKICAgIAogICAgCiAgICBlZGdlX2lkX2NvbG9ycyA8LSAKICAgICAgZWRnZV9kYXRhICU+JSAKICAgICAgZmlsdGVyKCFpcy5uYShjb2xvcikpICUkJQogICAgICBzZXRfbmFtZXMoY29sb3IsIGVkZ2VfaWQpCiAgICAKICAgIAogICAgZm9yKHJhbmtfcmFkIGluIDI6bWF4KGVkZ2VfZGF0YSRyYW5rX3JhZGl1cykpIHsKICAgICAgZWRnZV9pZF9jb2xvcnNfbmV3IDwtIAogICAgICAgIGxlZnRfam9pbihlZGdlX2RhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGVkZ2VfaWQsIHJhZGl1cywgeGVuZF9tLCB5ZW5kX20sIHJhbmtfcmFkaXVzKSAlPiUKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIocmFua19yYWRpdXMgPT0gcmFua19yYWQpLAogICAgICAgICAgICAgICAgICBlZGdlX2RhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGVkZ2VfaWQsIHJhZGl1cywgeF9tLCB5X20sIHJhbmtfcmFkaXVzKSAlPiUKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIocmFua19yYWRpdXMgPCByYW5rX3JhZCksCiAgICAgICAgICAgICAgICAgIGJ5ID0gYygieGVuZF9tIiA9ICJ4X20iLCAieWVuZF9tIiA9ICJ5X20iKSkgJT4lCiAgICAgICAgbGVmdF9qb2luKGVuZnJhbWUoZWRnZV9pZF9jb2xvcnMpLAogICAgICAgICAgICAgICAgICBieSA9IGMoImVkZ2VfaWQueSIgPSAibmFtZSIpKSAlPiUKICAgICAgICBncm91cF9ieShlZGdlX2lkLngpICU+JSAKICAgICAgICBzdW1tYXJpc2UoY29sb3IgPSBpZmVsc2Uobl9kaXN0aW5jdCh2YWx1ZSkgPT0gMSAmIGFueSh2YWx1ZSAhPSBkZWZhdWx0X2NvbG9yKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcih1bmlxdWUodmFsdWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdF9jb2xvcikpICUkJQogICAgICAgIHNldF9uYW1lcyhjb2xvciwgZWRnZV9pZC54KQogICAgICBlZGdlX2lkX2NvbG9ycyA8LSAKICAgICAgICBjKGVkZ2VfaWRfY29sb3JzLCBlZGdlX2lkX2NvbG9yc19uZXcpCiAgICB9CiAgICAKICAgIAogICAgCiAgICBnICsKICAgICAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IHdpZHRoX3JhbmdlKSsKICAgICAgZ2VvbV9lZGdlX2RpYWdvbmFsKGRhdGEgPSBlZGdlX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZWRnZV9jb2xvciA9IGFzLmNoYXJhY3RlcihlZGdlX2lkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX3dpZHRoID0gMSAtIHNxcnQoeGVuZF4yICsgeWVuZF4yKSksCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHJlbmd0aCA9IGFyY19zdHJlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikgKwogICAgICAKICAgICAgCiAgICAgIHNjYWxlX2VkZ2VfY29sb3JfbWFudWFsKHZhbHVlcyA9IGVkZ2VfaWRfY29sb3JzKSAgKwogICAgICBnJGRhdGEgJT4lCiAgICAgIGZpbHRlcihsYWJlbCAhPSAiIikgJT4lCiAgICAgIG11dGF0ZShkZWdyZWUgPSBjYXNlX3doZW4oeCA+PSAwIH4gYXNpbih5KSAqIDE4MCAvIHBpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPCAwIH4gMzYwIC0gYXNpbih5KSAqIDE4MCAvIHBpKSkgJT4lCiAgICAgIGxlZnRfam9pbihjb2xvcl9tYXBwaW5nICU+JQogICAgICAgICAgICAgICAgICBzZWxlY3QobGFiZWwgPSBsYWJlbF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yX2NvbCksCiAgICAgICAgICAgICAgICBieSA9ICJsYWJlbCIpICU+JQogICAgICAgICAgICAgICAge2dlb21fbm9kZV90ZXh0KGRhdGEgPSAuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IC4kZGVncmVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gaWZlbHNlKC4keCA8IDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSB0ZXh0X3NpemUpfSAgKwogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5kX3NjYWxlKHNjYWxlX2V4cGFuc2lvbikpICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuZF9zY2FsZShzY2FsZV9leHBhbnNpb24pKSArCiAgICAgIAogICAgICBjb29yZF9maXhlZCgpICsKICAgICAgdGhlbWVfdm9pZCgpCiAgfQpgYGAKCgpgYGB7cn0KCmhjbHVzdDRSTkFzZXFfd2FyZCA8LSBmdW5jdGlvbihkZiwgY29ycmVsYXRpb25fbWV0aG9kID0gInNwZWFybWFuIil7CiAgI3dpZGUgZGF0YWZyYW1lIGFzIGlucHV0IAogICN0byBnZXQgY29ycmVsYXRpb24gYmV0d2VlbiBzYW1wbGVzLCB3aGVyZSByb3dzIGFyZSBnZW5lcyBjb2x1bW5zIGFyZSBzYW1wbGVzCiAgI3RvIGdldCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGdlbmVzIGFjcm9zcyBzYW1wbGVzLCBpbnB1dCBkZiB3aXRoIGdlbmVzIGFzIGNvbHVtbnMKICAjY2FuIHVzZSBsYXRlciBmb3IgZGVuZG9ncmFtIG1ha2luZzogZ2dkZW5kcm9ncmFtKFtoY2x1c3Q0Uk5Bc2VxX3Jlc3VsdHNdLCByb3RhdGUgPSBGQUxTRSwgc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKQogIHNpbWlsYXJpdHkgPC0gY29yKGRmLCBtZXRob2Q9Y29ycmVsYXRpb25fbWV0aG9kLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCiAgZGlzc2ltaWxhcml0eSA8LSAxIC0gc2ltaWxhcml0eQogIGhjbCA8LSBoY2x1c3QoYXMuZGlzdChkaXNzaW1pbGFyaXR5KSwgIndhcmQuRDIiKQogIHJldHVybiAoaGNsKQp9IAoKdGlzc3VlX2RlbmRyb193YXJkIDwtIGhjbHVzdDRSTkFzZXFfd2FyZCh0bW1fdGlzc3VlICU+JSBtdXRhdGUodGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlX25hbWUpKSAlPiUgIHNwcmVhZCh0aXNzdWVfbmFtZSwgdG1tKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJ0YXJnZXRfaWQiKSkKCmNpcmN1bGFyX2RlbmRyb2dyYW1fcmV0aW5hc3R5bGVfMigKICBjbHVzdCA9IHRpc3N1ZV9kZW5kcm9fd2FyZCwgCiAgY29sb3JfbWFwcGluZyA9IG1ldGFkYXRhICU+JSAKICAgIHNlbGVjdCh0aXNzdWVfbmFtZSwgdGlzc3VlX2NvbG9yKSAlPiUgCiAgICBtdXRhdGUodGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlX25hbWUpKSwgCiAgbGFiZWxfY29sID0gInRpc3N1ZV9uYW1lIiwgCiAgY29sb3JfY29sID0gInRpc3N1ZV9jb2xvciIsIAogIHNjYWxlX2V4cGFuc2lvbiA9IGMoMC43LCAwLjcpLCAKICB0ZXh0X3NpemUgPSAyLjQsIAogIHdpZHRoX3JhbmdlID0gYygwLjUsIDQpLAogIGFyY19zdHJlbmd0aCA9IDAuNCwgCiAgZGVmYXVsdF9jb2xvciA9ICJncmF5ODAiKQoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3JldGluYWdyYW1fYWxsX3Rpc3N1ZV9jbHVzdF93YXJkLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKCmBgYAoKIyNGaWd1cmUgMUMgLSBTcGVhcm1hbiBoZWF0bWFwIChncm91cGVkIHRpc3N1ZSkKYGBge3J9CiMjU3BlYXJtYW4ncyByb2ggaGVhdG1hcCBhdCBncm91cGVkIHRpc3N1ZSBsZXZlbAoKaWYoZmlsZS5leGlzdHMoIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfY29uc2Vuc3VzX3Rpc3N1ZXMuY3N2IikpIHsKICBjb25zZW5zdXNfdG1tX3NwZWFybWFuIDwtIHJlYWRfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9zcGVhcm1hbl9jb3JyX2NvbnNlbnN1c190aXNzdWVzLmNzdiIpCn0gZWxzZSB7CiAgY29uc2Vuc3VzX3RtbV9zcGVhcm1hbiA8LSAgdG1tX2NvbnNlbnN1c190aXNzdWUgJT4lIAogICAgc3ByZWFkKGNvbnNlbnN1c190aXNzdWVfbmFtZSwgdG1tKSAlPiUgCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInRhcmdldF9pZCIpICU+JSAKICAgIGNvcihtZXRob2Q9InNwZWFybWFuIiwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSAlPiUgCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgYXNfdGliYmxlKHJvd25hbWVzID0gImNvbnNlbnN1c190aXNzdWVfbmFtZSIpCiAgd3JpdGVfY3N2KGNvbnNlbnN1c190bW1fc3BlYXJtYW4sIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfY29uc2Vuc3VzX3Rpc3N1ZXMuY3N2IikKfQoKY29uc2Vuc3VzX3RtbV9zcGVhcm1hbiAlPiUgCiAgcmVuYW1lX3dpdGgoc3RyX3RvX3NlbnRlbmNlLCAtMSkgJT4lIAogIG11dGF0ZShjb25zZW5zdXNfdGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UoY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygiY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lIikgJT4lIAogIHBoZWF0bWFwKAogICAjIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLAogICAgY2VsbGhlaWdodCA9IDgsCiAgICBjZWxsd2lkdGggPSA4LCAKICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgY29sb3IgPSB2aXJpZGlzOjppbmZlcm5vKDIwLCBkaXJlY3Rpb24gPSAtMSksCiAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIAogICAgKSAlPiUgCiAgYXMuZ2dwbG90KCkKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NwZWFybWFuX2NvcnJfY29uc2Vuc3VzX3Rpc3N1ZS5wZGYiLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCkKCmBgYAoKCiNGaWd1cmUgMgojI0ZpZ3VyZSAyQSAtIFNhbXBsZSBsZXZlbCBQQ0EgUGxvdApgYGB7cn0KCnNhbXBsZV9wY2EgPC0KICB0bW1fc2FtcGxlICU+JSAKICAKICAjIGdhdGhlcihzYW1wbGVfbmFtZSwgdG1tLCAtMSkgJT4lCiAgIyBncm91cF9ieSh0YXJnZXRfaWQpICU+JQogICMgbXV0YXRlKHNkID0gc2QodG1tKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgIyBmaWx0ZXIoc2QgPiAwKSAlPiUKICAjIHNlbGVjdCgtc2QpICU+JQogICMgc3ByZWFkKHNhbXBsZV9uYW1lLCB0bW0pICU+JQogIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0YXJnZXRfaWQiKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IDgpCgojc2FtcGxlX3BjYUBzY29yZXMKCnN1bW1hcnkoc2FtcGxlX3BjYSkKCnBsb3RfZGF0YSA8LSBzYW1wbGVfcGNhICU+JSAKICBzY29yZXMoKSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInNhbXBsZV9pZCIpICU+JSAKICBsZWZ0X2pvaW4obWV0YWRhdGEsCiAgICAgICAgICAgIGJ5ID0gYygic2FtcGxlX2lkIiA9ICJJRCIpKQoKCm9yZ2FuX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KG9yZ2FuX25hbWUsIG9yZ2FuX2NvbG9yKSAlPiUgdW5pcXVlKCkKcGFsIDwtICBvcmdhbl9jb2xvcnMkb3JnYW5fY29sb3IKcGFsIDwtIHNldE5hbWVzKHBhbCwgb3JnYW5fY29sb3JzJG9yZ2FuX25hbWUpCgpwbG90X2RhdGEgJT4lCiAgZ2dwbG90KGFlcyhQQzEsIFBDMikpICsKICBnZW9tX3BvaW50KGFlcyhmaWxsID0gb3JnYW5fbmFtZSksIGNvbG9yID0gImdyYXkyNSIsIGFscGhhID0gMC43LCBzaGFwZT0yMSwgc2l6ZSA9IDIsIHN0cm9rZSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKyAKICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNhbXBsZV9pZCksIHZqdXN0ID0gMSxoanVzdCA9IDAsIG51ZGdlX3kgPS0xKSArCiAgeGxhYihwYXN0ZSgiUEMxIiwgc2FtcGxlX3BjYUBSMlsxXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlKCJQQzIiLCBzYW1wbGVfcGNhQFIyWzJdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID1lbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KC0wLjAxLCAnY20nKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpKSAKCgojZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9wY2FfMS5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCiMgZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9wY2FfMV93X2ZpbHRlci5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCiMgCiMgcGxvdF9kYXRhICU+JQojICAgZ2dwbG90KGFlcyhQQzEsIFBDMikpICsKIyAgIGdlb21fcG9pbnQoYWVzKGZpbGwgPSBvcmdhbl9uYW1lKSwgY29sb3IgPSAiZ3JheTI1IiwgYWxwaGEgPSAwLjcsIHNoYXBlPTIxLCBzaXplID0gMiwgc3Ryb2tlID0gMC41KSArCiMgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsgCiMgICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNhbXBsZV9pZCksIHZqdXN0ID0gMSxoanVzdCA9IDAsIG51ZGdlX3kgPS0xKSArCiMgICB4bGFiKHBhc3RlKCJQQzEiLCBzYW1wbGVfcGNhQFIyWzFdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwojICAgeWxhYihwYXN0ZSgiUEMyIiwgc2FtcGxlX3BjYUBSMlsyXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKIyAgIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPWVsZW1lbnRfYmxhbmsoKSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiMgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMiwgYnlyb3cgPSBUUlVFKSkgCiMgCiMgI2dnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9zYW1wbGVfcGNhXzIucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQojICMgZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9wY2FfMl93X2ZpbHRlci5wZGYiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcpCgoKYGBgCgojIyNFeHRyYSBQQ0EgcGxvdHMgbm90IGluIHJlcG9ydApgYGB7cn0Kc2FtcGxlX3BjYSA8LQogIHRtbV9zYW1wbGUgJT4lIAogICMgZ2F0aGVyKHNhbXBsZV9uYW1lLCB0bW0sIC0xKSAlPiUgCiAgIyBncm91cF9ieSh0YXJnZXRfaWQpICU+JQogICMgbXV0YXRlKHNkID0gc2QodG1tKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lIAogICMgZmlsdGVyKHNkID4gMCkgJT4lIAogICMgc2VsZWN0KC1zZCkgJT4lIAogICMgc3ByZWFkKHNhbXBsZV9uYW1lLCB0bW0pICU+JSAKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCl7bG9nMTAoeCsxKX0pICU+JQogIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgc2NhbGUoKSAlPiUKICB0KCkgJT4lCiAgcGNhKG5QY3MgPSA4KQoKI3NhbXBsZV9wY2FAc2NvcmVzCgpzdW1tYXJ5KHNhbXBsZV9wY2EpCgpwbG90X2RhdGEgPC0gc2FtcGxlX3BjYSAlPiUgCiAgc2NvcmVzKCkgJT4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJzYW1wbGVfaWQiKSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhLAogICAgICAgICAgICBieSA9IGMoInNhbXBsZV9pZCIgPSAiSUQiKSkKCgpyZWdpb25fY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QocmVnaW9uX3Rpc3N1ZV9uYW1lLCByZWdpb25fdGlzc3VlX2NvbG9yLCBvcmdhbl9uYW1lKSAlPiUgdW5pcXVlKCkgJT4lIGZpbHRlcihvcmdhbl9uYW1lID09ICJCcmFpbiIpICU+JSBzZWxlY3QoLW9yZ2FuX25hbWUpCnBhbCA8LSAgcmVnaW9uX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl9jb2xvcnMkcmVnaW9uX3Rpc3N1ZV9uYW1lKQoKcGxvdF9kYXRhICU+JQogIGdncGxvdChhZXMoUEMxLCBQQzIpKSArCiAgZ2VvbV9wb2ludChhZXMoZmlsbCA9IHJlZ2lvbl90aXNzdWVfbmFtZSksICBjb2xvciA9ICJncmF5MjUiLCBhbHBoYSA9IDAuNywgc2hhcGU9MjEsIHNpemUgPSAyLCBzdHJva2UgPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwsIG5hLnZhbHVlID0gIiNGRkZGRkYiKSArIAogIHhsaW0oLTc1LDApICsKICB5bGltKC0xMCw1KSArCiAgeGxhYihwYXN0ZSgiUEMxIiwgc2FtcGxlX3BjYUBSMlsxXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlKCJQQzIiLCBzYW1wbGVfcGNhQFIyWzJdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID1lbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KC0wLjAxLCAnY20nKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyLCBieXJvdyA9IFRSVUUpKSAKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9icmFpbl93X2FsbF9zYW1wbGVfcGNhLnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKCgpgYGAKCmBgYHtyfQoKYnJhaW5fcGNhIDwtCiAgdG1tX3NhbXBsZSAlPiUgc2VsZWN0KGModGFyZ2V0X2lkLCBtZXRhZGF0YSAlPiUgZmlsdGVyKG9yZ2FuX25hbWUgPT0iQnJhaW4iKSAlPiUgLiRJRCkpJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0YXJnZXRfaWQiKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IDgpCgojYnJhaW5fcGNhQHNjb3JlcwoKCnBsb3RfZGF0YSA8LSBicmFpbl9wY2EgJT4lIAogIHNjb3JlcygpICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAic2FtcGxlX2lkIikgJT4lIAogIGxlZnRfam9pbihtZXRhZGF0YSwKICAgICAgICAgICAgYnkgPSBjKCJzYW1wbGVfaWQiID0gIklEIikpCgoKcmVnaW9uX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KHJlZ2lvbl90aXNzdWVfbmFtZSwgcmVnaW9uX3Rpc3N1ZV9jb2xvcikgJT4lIHVuaXF1ZSgpCnBhbCA8LSAgcmVnaW9uX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl9jb2xvcnMkcmVnaW9uX3Rpc3N1ZV9uYW1lKQoKcGxvdF9kYXRhICU+JQogIGdncGxvdChhZXMoUEMxLCBQQzIpKSArCiAgZ2VvbV9wb2ludChhZXMoZmlsbCA9IHJlZ2lvbl90aXNzdWVfbmFtZSksIGNvbG9yID0gImdyYXkyNSIsIGFscGhhID0gMC43LCBzaGFwZT0yMSwgc2l6ZSA9IDIsIHN0cm9rZSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKyAKICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNhbXBsZV9pZCksIHZqdXN0ID0gMSxoanVzdCA9IDAsIG51ZGdlX3kgPS0xKSArCiAgeGxhYihwYXN0ZSgiUEMxIiwgYnJhaW5fcGNhQFIyWzFdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUoIlBDMiIsIGJyYWluX3BjYUBSMlsyXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgtMC4zLCAnY20nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuc3BhY2luZy54ID0gdW5pdCgtMC4wMSwgJ2NtJykpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMiwgYnlyb3cgPSBUUlVFKSkgCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvZGF0YV9wcmVzZW50YXRpb24vYnJhaW5fc2FtcGxlX3BjYS5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCgoKCmBgYAoKIyNGaWd1cmUgMkIgLSBVTUFQIFBsb3QKYGBge3J9CndpZGVfZGF0YSA9IHRtbV9zYW1wbGUgCnNlZWQgPSA0Cm5fZXBvY2hzID0gMTAwMApuX25laWdoYm9ycyA9IDE1CnNldC5zZWVkKHNlZWQpCiAgICAKcGNhX3JlcyA8LQogIHdpZGVfZGF0YSAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpe2xvZzEwKHgrMSl9KSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKGNvbG5hbWVzKHdpZGVfZGF0YSlbMV0pICU+JQogIHNjYWxlKCkgJT4lCiAgdCgpICU+JQogIHBjYShuUGNzID0gZGltKC4pWzFdKQogICAgCiAgICAKcGNfbGltIDwtCiAgd2hpY2gocGNhX3Jlc0BSMmN1bSA+IDAuOClbMV0KCnBjX2xpbV9zZCA8LQogIHJldih3aGljaChwY2FfcmVzQHNEZXYgPiAxKSlbMV0KCnNldC5zZWVkKHNlZWQpCnVtYXBfcmVzIDwtIHBjYV9yZXNAc2NvcmVzWywgMTpwY19saW1dICU+JQogIHVtYXAobl9uZWlnaGJvcnMgPSBuX25laWdoYm9ycywKICAgICAgIG5fZXBvY2hzID0gbl9lcG9jaHMpICU+JQogICAgICAgI21ldHJpYyA9ICJjb3JyZWxhdGlvbiIpICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHNldF9uYW1lcyhwYXN0ZTAoIlVNQVAiLCAxOm5jb2woLikpKSAlPiUKICBtdXRhdGUoc2FtcGxlID0gcm93bmFtZXMocGNhX3Jlc0BzY29yZXMpKSAlPiUKICBzZWxlY3Qoc2FtcGxlLCBldmVyeXRoaW5nKCkpICU+JSAKICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5ID0gYygic2FtcGxlIiA9ICJJRCIpKQoKb3JnYW5fY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3Qob3JnYW5fbmFtZSwgb3JnYW5fY29sb3IpICU+JSB1bmlxdWUoKQpwYWwgPC0gIG9yZ2FuX2NvbG9ycyRvcmdhbl9jb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBvcmdhbl9jb2xvcnMkb3JnYW5fbmFtZSkKCiMgdW1hcF9yZXMgJT4lICBnZ3Bsb3QoYWVzKFVNQVAxLCBVTUFQMiwgIGNvbG9yID0gb3JnYW5fbmFtZSkpICsKIyAgICBnZW9tX3BvaW50KGFscGhhID0gMC44KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpCnVtYXBfcmVzICU+JSAgZ2dwbG90KGFlcyhVTUFQMSwgVU1BUDIpKSArCiAgZ2VvbV9wb2ludCgKICAgIGFlcyhmaWxsID0gb3JnYW5fbmFtZSksCiAgICBjb2xvciA9ICJncmF5MjUiLAogICAgYWxwaGEgPSAwLjcsCiAgICBzaGFwZSA9IDIxLAogICAgc2l6ZSA9IDIsCiAgICBzdHJva2UgPSAwLjUKICApICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSArIAogICMgdGhlbWVfYncoKSArIHRoZW1lKAogICMgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwKICAjICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpCiAgIyApCiAgdGhlbWVfY2xhc3NpYygpICsgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLTAuMywgJ2NtJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIsIGJ5cm93ID0gVFJVRSkpIAoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV91bWFwX2V1Y2xpZGVhbi5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUuNSkKYGBgCgojI0ZpZ3VyZSAyQyAtIEJyYWluIFVtYXAgUGxvdApgYGB7cn0KI1Bsb3QgZm9jdXNpbmcgb25seSBvbiBicmFpbiBzYW1wbGVzLCBidXQgVU1BUCB3YXMgYmFzZWQgb24gdGhlIHdob2xlIHNhbXBsZSBzZXQsIG5vdCBvbmx5IGJyaWFuIHNhbXBsZXMuCgp3aWRlX2RhdGEgPSB0bW1fc2FtcGxlIApzZWVkID0gNApuX2Vwb2NocyA9IDEwMDAKbl9uZWlnaGJvcnMgPSAxNQpzZXQuc2VlZChzZWVkKQogICAgCnBjYV9yZXMgPC0KICB3aWRlX2RhdGEgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcyhjb2xuYW1lcyh3aWRlX2RhdGEpWzFdKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IGRpbSguKVsxXSkKICAgIAogICAgCnBjX2xpbSA8LQogIHdoaWNoKHBjYV9yZXNAUjJjdW0gPiAwLjgpWzFdCgpwY19saW1fc2QgPC0KICByZXYod2hpY2gocGNhX3Jlc0BzRGV2ID4gMSkpWzFdCgpzZXQuc2VlZChzZWVkKQp1bWFwX3JlcyA8LSBwY2FfcmVzQHNjb3Jlc1ssIDE6cGNfbGltXSAlPiUKICB1bWFwKG5fbmVpZ2hib3JzID0gbl9uZWlnaGJvcnMsCiAgICAgICBuX2Vwb2NocyA9IG5fZXBvY2hzKSAlPiUKICAgICAgICNtZXRyaWMgPSAiY29ycmVsYXRpb24iKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBzZXRfbmFtZXMocGFzdGUwKCJVTUFQIiwgMTpuY29sKC4pKSkgJT4lCiAgbXV0YXRlKHNhbXBsZSA9IHJvd25hbWVzKHBjYV9yZXNAc2NvcmVzKSkgJT4lCiAgc2VsZWN0KHNhbXBsZSwgZXZlcnl0aGluZygpKSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhLCBieSA9IGMoInNhbXBsZSIgPSAiSUQiKSkKCgpyZWdpb25fY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QocmVnaW9uX3Rpc3N1ZV9uYW1lLCByZWdpb25fdGlzc3VlX2NvbG9yLCBvcmdhbl9uYW1lKSAlPiUgdW5pcXVlKCkgJT4lIGZpbHRlcihvcmdhbl9uYW1lID09ICJCcmFpbiIpICU+JSBzZWxlY3QoLW9yZ2FuX25hbWUpCnBhbCA8LSAgcmVnaW9uX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl9jb2xvcnMkcmVnaW9uX3Rpc3N1ZV9uYW1lKQojIHVtYXBfcmVzICU+JSAgZ2dwbG90KGFlcyhVTUFQMSwgVU1BUDIsICBjb2xvciA9IG9yZ2FuX25hbWUpKSArCiMgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuOCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsKQp1bWFwX3JlcyAlPiUgIGdncGxvdChhZXMoVU1BUDEsIFVNQVAyKSkgKwogIGdlb21fcG9pbnQoCiAgICBhZXMoZmlsbCA9IHJlZ2lvbl90aXNzdWVfbmFtZSksCiAgICBjb2xvciA9ICJncmF5MjUiLAogICAgYWxwaGEgPSAwLjcsCiAgICBzaGFwZSA9IDIxLAogICAgc2l6ZSA9IDIsCiAgICBzdHJva2UgPSAwLjUKICApICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsLCBuYS52YWx1ZSA9ICIjRkZGRkZGIikgKyAKICAjIHRoZW1lX2J3KCkgKyB0aGVtZSgKICAjICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICMgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICMgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICMgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksCiAgIyAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQogICMgKQogIHhsaW0oLTExLC02KSArCiAgeWxpbSgtOCwtMikgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID1lbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KC0wLjAxLCAnY20nKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyLCBieXJvdyA9IFRSVUUpKSAKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9zYW1wbGVfZm9jdXNfYnJhaW5fdW1hcF9ldWNsaWRlYW4ucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1LjUpCgpgYGAKIyMjRXh0cmEgVU1BUCBwbG90IG5vdCBpbiByZXBvcnQKYGBge3J9CiNVTUFQIHBsb3QgYmFzZWQgb25seSBvbiBicmFpbiBzYW1wbGVzLCB0aHVzIGRpZmZlcmVudCB0aGFuIHRoZSBwbG90IGFib3ZlLgoKd2lkZV9kYXRhID0gdG1tX3NhbXBsZSAlPiUgc2VsZWN0KGModGFyZ2V0X2lkLCBtZXRhZGF0YSAlPiUgZmlsdGVyKG9yZ2FuX25hbWUgPT0gIkJyYWluIikgJT4lIC4kSUQpKQpzZWVkID0gNApuX2Vwb2NocyA9IDEwMDAKbl9uZWlnaGJvcnMgPSAxNQpzZXQuc2VlZChzZWVkKQogICAgCnBjYV9yZXMgPC0KICB3aWRlX2RhdGEgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcyhjb2xuYW1lcyh3aWRlX2RhdGEpWzFdKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IGRpbSguKVsxXSkKICAgIAogICAgCnBjX2xpbSA8LQogIHdoaWNoKHBjYV9yZXNAUjJjdW0gPiAwLjgpWzFdCgpwY19saW1fc2QgPC0KICByZXYod2hpY2gocGNhX3Jlc0BzRGV2ID4gMSkpWzFdCgpzZXQuc2VlZChzZWVkKQp1bWFwX3JlcyA8LSBwY2FfcmVzQHNjb3Jlc1ssIDE6cGNfbGltXSAlPiUKICB1bWFwKG5fbmVpZ2hib3JzID0gbl9uZWlnaGJvcnMsCiAgICAgICBuX2Vwb2NocyA9IG5fZXBvY2hzKSAlPiUKICAgICAgICNtZXRyaWMgPSAiY29ycmVsYXRpb24iKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBzZXRfbmFtZXMocGFzdGUwKCJVTUFQIiwgMTpuY29sKC4pKSkgJT4lCiAgbXV0YXRlKHNhbXBsZSA9IHJvd25hbWVzKHBjYV9yZXNAc2NvcmVzKSkgJT4lCiAgc2VsZWN0KHNhbXBsZSwgZXZlcnl0aGluZygpKSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhLCBieSA9IGMoInNhbXBsZSIgPSAiSUQiKSkKCnJlZ2lvbl90aXNzdWVfY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QocmVnaW9uX3Rpc3N1ZV9uYW1lLCByZWdpb25fdGlzc3VlX2NvbG9yKSAlPiUgdW5pcXVlKCkKcGFsIDwtICByZWdpb25fdGlzc3VlX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl90aXNzdWVfY29sb3JzJHJlZ2lvbl90aXNzdWVfbmFtZSkKCiMgdW1hcF9yZXMgJT4lICBnZ3Bsb3QoYWVzKFVNQVAxLCBVTUFQMiwgIGNvbG9yID0gb3JnYW5fbmFtZSkpICsKIyAgICBnZW9tX3BvaW50KGFscGhhID0gMC44KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpCnVtYXBfcmVzICU+JSAgZ2dwbG90KGFlcyhVTUFQMSwgVU1BUDIpKSArCiAgZ2VvbV9wb2ludCgKICAgIGFlcyhmaWxsID0gcmVnaW9uX3Rpc3N1ZV9uYW1lKSwKICAgIGNvbG9yID0gImdyYXkyNSIsCiAgICBhbHBoYSA9IDAuNywKICAgIHNoYXBlID0gMjEsCiAgICBzaXplID0gMiwKICAgIHN0cm9rZSA9IDAuNQogICkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsgCiAgIyB0aGVtZV9idygpICsgdGhlbWUoCiAgIyAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICMgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICAjICkKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICMgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLTAuMywgJ2NtJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEsIGJ5cm93ID0gVFJVRSkpIAoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9icmFpbl91bWFwX2V1Y2xpZGVhbl9sLnBkZiIsIHdpZHRoID0gNS41LCBoZWlnaHQgPSAzKQpgYGAKIyNGaWd1cmUgMkQgLSAgZ3JvdXBlZCBzYW1wbGUgdG8gc2FtcGxlIGNvcnJlbGF0aW9uIHBsb3RzCmBgYHtyfQppZihmaWxlLmV4aXN0cygiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fc2FtcGxlLmNzdiIpKSB7CiAgc2FtcGxlX3RtbV9zcGVhcm1hbiA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fc2FtcGxlLmNzdiIpCn0gZWxzZSB7CiAgc2FtcGxlX3RtbV9zcGVhcm1hbiA8LSAgdG1tX3NhbXBsZSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gInNhbXBsZV9uYW1lIikKICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZShzYW1wbGVfdG1tX3NwZWFybWFuKSAlPiUgYXNfdGliYmxlKCksIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX3NhbXBsZS5jc3YiKQp9Cgpjb3JyZWxhdGlvbl90b19kaWZmZXJlbnRfb3JnYW5zIDwtIHRpYmJsZShzYW1wbGVfbmFtZSA9IGMoKSwgY29ycmVsYXRpb24gPSBjKCkpCmZvciAoc2FtcGxlIGluIG1ldGFkYXRhJElEKXsKICBzYW1wbGVfb3JnYW4gPC0gbWV0YWRhdGEgJT4lIGZpbHRlcihJRCA9PSBzYW1wbGUpICU+JSAuJG9yZ2FuX25hbWUgJT4lIHVuaXF1ZSgpCiAgZGlmZmVyZW50X29yZ2FuX3NhbXBsZXMgPC0gbWV0YWRhdGEgJT4lIGZpbHRlcihvcmdhbl9uYW1lICE9IHNhbXBsZV9vcmdhbikgJT4lIC4kSUQKICBzYW1wbGVfY29ycmVsYXRpb24gPC0gc2FtcGxlX3RtbV9zcGVhcm1hbiAlPiUgZmlsdGVyKHNhbXBsZV9uYW1lICVpbiUgZGlmZmVyZW50X29yZ2FuX3NhbXBsZXMpICU+JSBzZWxlY3QoInNhbXBsZV9uYW1lIiwgc2FtcGxlKSAlPiUgcmVuYW1lKCJjb3JyZWxhdGlvbiIgPSBzYW1wbGUpCiAgY29ycmVsYXRpb25fdG9fZGlmZmVyZW50X29yZ2FucyA8LSByYmluZChjb3JyZWxhdGlvbl90b19kaWZmZXJlbnRfb3JnYW5zLCBzYW1wbGVfY29ycmVsYXRpb24pIAp9Cgpjb3JyZWxhdGlvbl90b19zYW1lX29yZ2FuIDwtIHRpYmJsZShzYW1wbGVfbmFtZSA9IGMoKSwgY29ycmVsYXRpb24gPSBjKCkpCmZvciAoc2FtcGxlIGluIG1ldGFkYXRhJElEKXsKICBzYW1wbGVfb3JnYW4gPC0gbWV0YWRhdGEgJT4lIGZpbHRlcihJRCA9PSBzYW1wbGUpICU+JSAuJG9yZ2FuX25hbWUgJT4lIHVuaXF1ZSgpCiAgc2FtZV9vcmdhbl9zYW1wbGVzIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIob3JnYW5fbmFtZSA9PSBzYW1wbGVfb3JnYW4pICU+JSBmaWx0ZXIoSUQgIT0gc2FtcGxlKSAlPiUgLiRJRAogIHNhbXBsZV9jb3JyZWxhdGlvbiA8LSBzYW1wbGVfdG1tX3NwZWFybWFuICU+JSBmaWx0ZXIoc2FtcGxlX25hbWUgJWluJSBzYW1lX29yZ2FuX3NhbXBsZXMpICU+JSBzZWxlY3QoInNhbXBsZV9uYW1lIiwgc2FtcGxlKSAlPiUgcmVuYW1lKCJjb3JyZWxhdGlvbiIgPSBzYW1wbGUpCiAgY29ycmVsYXRpb25fdG9fc2FtZV9vcmdhbiA8LSByYmluZChjb3JyZWxhdGlvbl90b19zYW1lX29yZ2FuLCBzYW1wbGVfY29ycmVsYXRpb24pIAp9Cgpjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZSA8LSB0aWJibGUoc2FtcGxlX25hbWUgPSBjKCksIGNvcnJlbGF0aW9uID0gYygpKQpmb3IgKHNhbXBsZSBpbiBtZXRhZGF0YSRJRCl7CiAgc2FtcGxlX3Rpc3N1ZSA8LSBtZXRhZGF0YSAlPiUgZmlsdGVyKElEID09IHNhbXBsZSkgJT4lIC4kdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCiAgc2FtZV90aXNzdWVfc2FtcGxlcyA8LSBtZXRhZGF0YSAlPiUgZmlsdGVyKHRpc3N1ZV9uYW1lID09IHNhbXBsZV90aXNzdWUpICU+JSBmaWx0ZXIoSUQgIT0gc2FtcGxlKSAlPiUgLiRJRAogIHNhbXBsZV9jb3JyZWxhdGlvbiA8LSBzYW1wbGVfdG1tX3NwZWFybWFuICU+JSBmaWx0ZXIoc2FtcGxlX25hbWUgJWluJSBzYW1lX3Rpc3N1ZV9zYW1wbGVzKSAlPiUgc2VsZWN0KCJzYW1wbGVfbmFtZSIsIHNhbXBsZSkgJT4lIHJlbmFtZSgiY29ycmVsYXRpb24iID0gc2FtcGxlKQogIGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlIDwtIHJiaW5kKGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlLCBzYW1wbGVfY29ycmVsYXRpb24pIAp9Cgpjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUgPC0gdGliYmxlKHNhbXBsZV9uYW1lID0gYygpLCBjb3JyZWxhdGlvbiA9IGMoKSwgdGlzc3VlX25hbWUgPSBjKCkpCmZvciAoc2FtcGxlIGluIG1ldGFkYXRhJElEKXsKICBzYW1wbGVfdGlzc3VlIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIoSUQgPT0gc2FtcGxlKSAlPiUgLiR0aXNzdWVfbmFtZSAlPiUgdW5pcXVlKCkKICBzYW1lX3Rpc3N1ZV9zYW1wbGVzIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIodGlzc3VlX25hbWUgPT0gc2FtcGxlX3Rpc3N1ZSkgJT4lIC4kSUQKICBzYW1wbGVfY29ycmVsYXRpb24gPC0gc2FtcGxlX3RtbV9zcGVhcm1hbiAlPiUgZmlsdGVyKHNhbXBsZV9uYW1lICVpbiUgc2FtZV90aXNzdWVfc2FtcGxlcykgJT4lIHNlbGVjdCgic2FtcGxlX25hbWUiLCBzYW1wbGUpICU+JSBmaWx0ZXIoc2FtcGxlX25hbWUgIT0gc2FtcGxlKSAlPiUgIGxlZnRfam9pbihtZXRhZGF0YSAlPiUgc2VsZWN0KElELCB0aXNzdWVfbmFtZSksIGJ5PSBjKCJzYW1wbGVfbmFtZSIgPSAiSUQiKSkgJT4lIHJlbmFtZSgiY29ycmVsYXRpb24iID0gc2FtcGxlKQogIGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlX2J5X3Rpc3N1ZSA8LSByYmluZChjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUsIHNhbXBsZV9jb3JyZWxhdGlvbikgCn0KCmNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlX2J5X3Rpc3N1ZSA8LSBjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUgJT4lIAogIG11dGF0ZSh0aXNzdWVfbmFtZSA9IHN0cl90b19zZW50ZW5jZSh0aXNzdWVfbmFtZSkpICU+JSAKICBncm91cF9ieSh0aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShtaW4gPSBtaW4oY29ycmVsYXRpb24pKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKG1pbikKCnAxIDwtIGNvcnJlbGF0aW9uX3RvX2RpZmZlcmVudF9vcmdhbnMgJT4lCiAgZ2dwbG90KGFlcyhjb3JyZWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSAgKyB4bGltKDAuNSwxKSsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCJncmF5OTAiKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55LmxlZnQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpICsgCiAgZ2d0aXRsZSgiRGlmZmVyZW50IG9yZ2FucyIpCgpwMiA8LSBjb3JyZWxhdGlvbl90b19zYW1lX29yZ2FuICU+JQogIGdncGxvdChhZXMoY29ycmVsYXRpb24pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKyB4bGltKDAuNSwxKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgiZ3JheTkwIiksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IDAsIGFkZCA9IDApKSArCiAgZ2d0aXRsZSgiU2FtZSBvcmdhbnMiKQoKcDMgPC0gY29ycmVsYXRpb25fdG9fc2FtZV90aXNzdWUgJT4lCiAgZ2dwbG90KGFlcyhjb3JyZWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArIHhsaW0oMC41LDEpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoImdyYXk5MCIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLCBhZGQgPSAwKSkgKwogIGdndGl0bGUoIlNhbWUgdGlzc3VlIikKCnA0IDwtIGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlX2J5X3Rpc3N1ZSAlPiUgCiAgbXV0YXRlKHRpc3N1ZV9uYW1lID0gZmFjdG9yKHRpc3N1ZV9uYW1lLCBjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUkdGlzc3VlX25hbWUgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZSgpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGNvcnJlbGF0aW9uLCB5ID0gdGlzc3VlX25hbWUpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2l6ZT0wLjMpICsgeGxpbSgwLjUsMSkgKwogIHRoZW1lX2J3KCkgKyAKICB4bGFiICgiU3BlYXJtYW4ncyByb2giKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKQoKcDEgLyBwMiAvIHAzIC8gcDQgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxLCAxLCAxNSkpCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvZGF0YV9wcmVzZW50YXRpb24vc3BlYXJtYW5fY29ycl9zYW1wbGVfdnNfdGlzc3VlX29yZ2FuLnBkZiIsIGhlaWdodCA9IDE0LCB3aWR0aCA9IDQpCmBgYAoKI1NwZWNpZmljaXR5IGFuZCBkaXN0cmlidXRpb24gYW5ub3RhdGlvbgojI0Zvcm11bGFzCmBgYHtyfQojY2FsY3VsYXRlX3RhdV9zY29yZSBhbmQgaHBhX2dlbmVfY2xhc3NpZmljYXRpb24gdGFrZW4gZnJvbSBNYXggS2FybHNzb24KY2FsY3VsYXRlX3RhdV9zY29yZSA8LSAKICBmdW5jdGlvbih3aWRlX2RhdGEpIHsKICAgIG1heF9leHAgPC0gCiAgICAgIGFwcGx5KHdpZGVfZGF0YSwKICAgICAgICAgICAgTUFSR0lOID0gMSwKICAgICAgICAgICAgZnVuY3Rpb24oeCkgbWF4KHgsIG5hLnJtID0gVCkpCiAgICAKICAgIE4gPC0gCiAgICAgIGFwcGx5KHdpZGVfZGF0YSwKICAgICAgICAgICAgTUFSR0lOID0gMSwKICAgICAgICAgICAgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCFpcy5uYSh4KSkpKQogICAgCiAgICBleHByZXNzaW9uX3N1bSA8LSAKICAgICAgd2lkZV9kYXRhICU+JSAKICAgICAgc3dlZXAoTUFSR0lOID0gMSwgCiAgICAgICAgICAgIFNUQVRTID0gbWF4X2V4cCwgCiAgICAgICAgICAgIEZVTiA9IGAvYCkgJT4lIAogICAgICB7MSAtIC59ICU+JSAKICAgICAgYXBwbHkoTUFSR0lOID0gMSwKICAgICAgICAgICAgZnVuY3Rpb24oeCkgc3VtKHgsIG5hLnJtID0gVCkpCiAgICAKICAgIAogICAgdGF1X3Njb3JlIDwtIAogICAgICAoZXhwcmVzc2lvbl9zdW0gLyAoTiAtIDEpKSAlPiUgCiAgICAgIGVuZnJhbWUoImdlbmUiLCAidGF1X3Njb3JlIikKICAgIAogICAgdGF1X3Njb3JlCiAgfQoKaHBhX2dlbmVfY2xhc3NpZmljYXRpb24gPC0gCiAgI2ZlZWQgaW4gbG9uZyBkYXRhCiAgZnVuY3Rpb24oZGF0YSwgZXhwcmVzc2lvbl9jb2wsIHRpc3N1ZV9jb2wsIGdlbmVfY29sLCBlbnJfZm9sZCwgbWF4X2dyb3VwX24sIGRldF9saW0gPSAxKSB7CiAgICBkYXRhXyA8LSAKICAgICAgZGF0YSAlPiUgCiAgICAgIHNlbGVjdChnZW5lID0gZ2VuZV9jb2wsCiAgICAgICAgICAgICBleHByZXNzaW9uID0gZXhwcmVzc2lvbl9jb2wsCiAgICAgICAgICAgICB0aXNzdWUgPSB0aXNzdWVfY29sKSAlPiUgCiAgICAgIG11dGF0ZShleHByZXNzaW9uID0gcm91bmQoZXhwcmVzc2lvbiwgNCkpIAogICAgCiAgICBpZihhbnkoaXMubmEoZGF0YV8kZXhwcmVzc2lvbikpKSBzdG9wKCJOQXMgaW4gZXhwcmVzc2lvbiBjb2x1bW4iKQogICAgaWYoYW55KGlzLm5hKGRhdGFfJGdlbmUpKSkgc3RvcCgiTkFzIGluIGdlbmUgY29sdW1uIikKICAgIGlmKGFueShpcy5uYShkYXRhXyR0aXNzdWUpKSkgc3RvcCgiTkFzIGluIHRpc3N1ZSBjb2x1bW4iKQogICAgCiAgICBuX2dyb3VwcyA8LSBsZW5ndGgodW5pcXVlKGRhdGFfJHRpc3N1ZSkpCiAgICAKICAgIGdlbmVfY2xhc3NfaW5mbyA8LSAKICAgICAgZGF0YV8gJT4lCiAgICAgIGdyb3VwX2J5KGdlbmUpICU+JQogICAgICBzdW1tYXJpc2UoCiAgICAgICAgCiAgICAgICAgIyBHZW5lIGV4cHJlc3Npb24gZGlzdHJpYnV0aW9uIG1ldHJpY3MKICAgICAgICBtZWFuX2V4cCA9IG1lYW4oZXhwcmVzc2lvbiwgbmEucm0gPSBUKSwKICAgICAgICBtaW5fZXhwID0gbWluKGV4cHJlc3Npb24sIG5hLnJtID0gVCksCiAgICAgICAgbWF4X2V4cCA9IG1heChleHByZXNzaW9uLCBuYS5ybSA9IFQpLCAKICAgICAgICBtYXhfMm5kID0gc29ydChleHByZXNzaW9uKVtsZW5ndGgoZXhwcmVzc2lvbiktMV0sCiAgICAgICAgCiAgICAgICAgIyBFeHByZXNzaW9uIGZyZXF1ZW5jeSBtZXRyaWNzCiAgICAgICAgbl9leHAgPSBsZW5ndGgod2hpY2goZXhwcmVzc2lvbiA+PSBkZXRfbGltKSksCiAgICAgICAgZnJhY19leHAgPSBuX2V4cC9sZW5ndGgoZXhwcmVzc2lvblshaXMubmEoZXhwcmVzc2lvbildKSoxMDAsCiAgICAgICAgCiAgICAgICAgIyBMaW1pdCBvZiBlbmhhbmNlbWVudCBtZXRyaWNzCiAgICAgICAgbGltID0gbWF4X2V4cC9lbnJfZm9sZCwgCiAgICAgICAgCiAgICAgICAgZXhwc19vdmVyX2xpbSA9IGxpc3QoZXhwcmVzc2lvblt3aGljaChleHByZXNzaW9uID49IGxpbSAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSldKSwKICAgICAgICBuX292ZXIgPSBsZW5ndGgoZXhwc19vdmVyX2xpbVtbMV1dKSwgCiAgICAgICAgbWVhbl9vdmVyID0gbWVhbihleHBzX292ZXJfbGltW1sxXV0pLAogICAgICAgIG1pbl9vdmVyID0gaWZlbHNlKG5fb3ZlciA9PSAwLCBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oZXhwc19vdmVyX2xpbVtbMV1dKSksCiAgICAgICAgCiAgICAgICAgbWF4X3VuZGVyX2xpbSA9IG1heChleHByZXNzaW9uW3doaWNoKGV4cHJlc3Npb24gPCBtaW5fb3ZlcildLCBkZXRfbGltKjAuMSksCiAgICAgICAgCiAgICAgICAgCiAgICAgICAgZXhwc19lbmhhbmNlZCA9IGxpc3Qod2hpY2goZXhwcmVzc2lvbi9tZWFuX2V4cCA+PSBlbnJfZm9sZCAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSkpLAogICAgICAgIAogICAgICAgIAogICAgICAgIAogICAgICAgIAogICAgICAgICMgRXhwcmVzc2lvbiBwYXR0ZXJucwogICAgICAgIGVucmljaG1lbnRfZ3JvdXAgPSBwYXN0ZShzb3J0KHRpc3N1ZVt3aGljaChleHByZXNzaW9uID49IGxpbSAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSldKSwgY29sbGFwc2U9IjsiKSwKICAgICAgICAKICAgICAgICBuX2VucmljaGVkID0gbGVuZ3RoKHRpc3N1ZVt3aGljaChleHByZXNzaW9uID49IGxpbSAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSldKSwKICAgICAgICBuX2VuaGFuY2VkID0gbGVuZ3RoKGV4cHNfZW5oYW5jZWRbWzFdXSksIAogICAgICAgIGVuaGFuY2VkX2luID0gcGFzdGUoc29ydCh0aXNzdWVbZXhwc19lbmhhbmNlZFtbMV1dXSksIGNvbGxhcHNlPSI7IiksCiAgICAgICAgbl9uYSA9IG5fZ3JvdXBzIC0gbGVuZ3RoKGV4cHJlc3Npb24pLAogICAgICAgIG1heF8ybmRfb3JfbGltID0gbWF4KG1heF8ybmQsIGRldF9saW0qMC4xKSwKICAgICAgICB0aXNzdWVzX25vdF9kZXRlY3RlZCA9IHBhc3RlKHNvcnQodGlzc3VlW3doaWNoKGV4cHJlc3Npb24gPCBkZXRfbGltKV0pLCBjb2xsYXBzZT0iOyIpLAogICAgICAgIHRpc3N1ZXNfZGV0ZWN0ZWQgPSBwYXN0ZShzb3J0KHRpc3N1ZVt3aGljaChleHByZXNzaW9uID49IGRldF9saW0pXSksIGNvbGxhcHNlPSI7IikpIAogICAgCiAgICAKICAgIGdlbmVfY2F0ZWdvcmllcyA8LSAKICAgICAgZ2VuZV9jbGFzc19pbmZvICU+JQogICAgICAKICAgICAgbXV0YXRlKAogICAgICAgIHNwZWNfY2F0ZWdvcnkgPSBjYXNlX3doZW4obl9leHAgPT0gMCB+ICJub3QgZGV0ZWN0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBHZW5lcyB3aXRoIGV4cHJlc3Npb24gZm9sZCB0aW1lcyBtb3JlIHRoYW4gYW55dGhpbmcgZWxzZSBhcmUgdGlzc3VlIGVucmljaGVkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfZXhwL21heF8ybmRfb3JfbGltID49IGVucl9mb2xkIH4gInRpc3N1ZSBlbnJpY2hlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdlbmVzIHdpdGggZXhwcmVzc2lvbiBmb2xkIHRpbWVzIG1vcmUgdGhhbiBvdGhlciB0aXNzdWVzIGluIGdyb3VwcyBvZiBtYXggZ3JvdXBfbiAtIDEgYXJlIGdyb3VwIGVucmljaGVkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfZXhwID49IGxpbSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fb3ZlciA8PSBtYXhfZ3JvdXBfbiAmIG5fb3ZlciA+IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX292ZXIvbWF4X3VuZGVyX2xpbSA+PSBlbnJfZm9sZCB+ICJncm91cCBlbnJpY2hlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdlbmVzIHdpdGggZXhwcmVzc2lvbiBpbiB0aXNzdWVzIGZvbGQgdGltZXMgbW9yZSB0aGFuIHRoZSBtZWFuIGFyZSB0aXNzdWUgZW5oYW5jZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9lbmhhbmNlZCA+IDAgfiAidGlzc3VlIGVuaGFuY2VkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgR2VuZXMgZXhwcmVzc2VkIHdpdGggbG93IHRpc3N1ZSBzcGVjaWZpY2l0eQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVCB+ICJsb3cgdGlzc3VlIHNwZWNpZmljaXR5IiksIAogICAgICAgIAogICAgICAgIAogICAgICAgIGRpc3RfY2F0ZWdvcnkgPSBjYXNlX3doZW4oZnJhY19leHAgPT0gMTAwIH4gImRldGVjdGVkIGluIGFsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcmFjX2V4cCA+PSAzMSB+ICJkZXRlY3RlZCBpbiBtYW55IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fZXhwID4gMSB+ICJkZXRlY3RlZCBpbiBzb21lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fZXhwID09IDEgfiAiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fZXhwID09IDAgfiAibm90IGRldGVjdGVkIiksCiAgICAgICAgCiAgICAgICAgc3BlY19zY29yZSA9IGNhc2Vfd2hlbihzcGVjX2NhdGVnb3J5ID09ICJ0aXNzdWUgZW5yaWNoZWQiIH4gbWF4X2V4cC9tYXhfMm5kX29yX2xpbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gImdyb3VwIGVucmljaGVkIiB+IG1lYW5fb3Zlci9tYXhfdW5kZXJfbGltLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gInRpc3N1ZSBlbmhhbmNlZCIgfiBtYXhfZXhwL21lYW5fZXhwKSkgCiAgICAKICAgIAogICAgCiAgICAKICAgICMjIyMjIFJlbmFtZSBhbmQgZm9ybWF0CiAgICBnZW5lX2NhdGVnb3JpZXMgJT4lCiAgICAgIG11dGF0ZShlbnJpY2hlZF90aXNzdWVzID0gY2FzZV93aGVuKHNwZWNfY2F0ZWdvcnkgJWluJSBjKCJ0aXNzdWUgZW5yaWNoZWQiLCAiZ3JvdXAgZW5yaWNoZWQiKSB+IGVucmljaG1lbnRfZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gInRpc3N1ZSBlbmhhbmNlZCIgfiBlbmhhbmNlZF9pbiksCiAgICAgICAgICAgICBuX2VucmljaGVkID0gY2FzZV93aGVuKHNwZWNfY2F0ZWdvcnkgJWluJSBjKCJ0aXNzdWUgZW5yaWNoZWQiLCAiZ3JvdXAgZW5yaWNoZWQiKSB+IG5fZW5yaWNoZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gInRpc3N1ZSBlbmhhbmNlZCIgfiBuX2VuaGFuY2VkKSkgJT4lCiAgICAgIHNlbGVjdChnZW5lLCAKICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnksIAogICAgICAgICAgICAgZGlzdF9jYXRlZ29yeSwgCiAgICAgICAgICAgICBzcGVjX3Njb3JlLAogICAgICAgICAgICAgbl9leHByZXNzZWQgPSBuX2V4cCwgCiAgICAgICAgICAgICBmcmFjdGlvbl9leHByZXNzZWQgPSBmcmFjX2V4cCwKICAgICAgICAgICAgIG1heF9leHAgPSBtYXhfZXhwLAogICAgICAgICAgICAgZW5yaWNoZWRfdGlzc3VlcywKICAgICAgICAgICAgIG5fZW5yaWNoZWQsCiAgICAgICAgICAgICBuX25hID0gbl9uYSwKICAgICAgICAgICAgIHRpc3N1ZXNfbm90X2RldGVjdGVkLAogICAgICAgICAgICAgdGlzc3Vlc19kZXRlY3RlZCkgCiAgICAKICB9CQoKYGBgCgojI0Fubm90YXRpb24KYGBge3J9CgojIFNwZWNpZmljaXR5IGNsYXNzaWZpY2F0aW9uIGF0IGNvbnNlbnN1cyBsZXZlbApjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgPC0gaHBhX2dlbmVfY2xhc3NpZmljYXRpb24oZGF0YSA9IHRtbV9jb25zZW5zdXNfdGlzc3VlLCBleHByZXNzaW9uX2NvbCA9ICJ0bW0iLCB0aXNzdWVfY29sID0gImNvbnNlbnN1c190aXNzdWVfbmFtZSIsIGdlbmVfY29sID0gInRhcmdldF9pZCIsIGVucl9mb2xkID0gNCwgbWF4X2dyb3VwX24gPSA1LCBkZXRfbGltID0gMSkKCmNvbnNlbnN1c190YXUgPC0gY2FsY3VsYXRlX3RhdV9zY29yZSh0bW1fY29uc2Vuc3VzX3Rpc3N1ZSAgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByZWFkKGNvbnNlbnN1c190aXNzdWVfbmFtZSwgdG1tKSU+JSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSklPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikpIAojY2F0ZWdvcnkgbm90IGRldGVjdGVkIGhhcyBhIHZlcnkgbm9pc3kgdGF1LCBzbyBubyB0YXUgc2NvcmUgZm9yIHRob3NlCmNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyA8LSBjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lIAogIGxlZnRfam9pbihjb25zZW5zdXNfdGF1LCBieSA9IGMoImdlbmUiID0gImdlbmUiKSkgJT4lIAogIG11dGF0ZSh0YXVfc2NvcmUgPSBpZmVsc2Uoc3BlY19jYXRlZ29yeSA9PSAibm90IGRldGVjdGVkIiwgTkEsIHRhdV9zY29yZSkpCgojYXQgcmVnaW9uIGxldmVsCmNsYXNzaWZpY2F0aW9uX3JlZ2lvbiA8LSBocGFfZ2VuZV9jbGFzc2lmaWNhdGlvbihkYXRhID0gdG1tX3JlZ2lvbl90aXNzdWUsIGV4cHJlc3Npb25fY29sID0gInRtbSIsIHRpc3N1ZV9jb2wgPSAicmVnaW9uX3Rpc3N1ZV9uYW1lIiwgZ2VuZV9jb2wgPSAidGFyZ2V0X2lkIiwgZW5yX2ZvbGQgPSA0LCBtYXhfZ3JvdXBfbiA9IDUsIGRldF9saW0gPSAxKQoKcmVnaW9uX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKHRtbV9yZWdpb25fdGlzc3VlICAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmVhZChyZWdpb25fdGlzc3VlX25hbWUsIHRtbSkgJT4lICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpe2xvZzEwKHgrMSl9KSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgKQoKY2xhc3NpZmljYXRpb25fcmVnaW9uIDwtIGNsYXNzaWZpY2F0aW9uX3JlZ2lvbiAlPiUgCiAgbGVmdF9qb2luKHJlZ2lvbl90YXUsIGJ5ID0gYygiZ2VuZSIgPSAiZ2VuZSIpKSAlPiUgCiAgbXV0YXRlKHRhdV9zY29yZSA9IGlmZWxzZShzcGVjX2NhdGVnb3J5ID09ICJub3QgZGV0ZWN0ZWQiLCBOQSwgdGF1X3Njb3JlKSkKCiNhdCB0aXNzdWUgbGV2ZWwKY2xhc3NpZmljYXRpb25fdGlzc3VlIDwtIGhwYV9nZW5lX2NsYXNzaWZpY2F0aW9uKGRhdGEgPSB0bW1fdGlzc3VlLCBleHByZXNzaW9uX2NvbCA9ICJ0bW0iLCB0aXNzdWVfY29sID0gInRpc3N1ZV9uYW1lIiwgZ2VuZV9jb2wgPSAidGFyZ2V0X2lkIiwgZW5yX2ZvbGQgPSA0LCBtYXhfZ3JvdXBfbiA9IDUsIGRldF9saW0gPSAxKQoKdGlzc3VlX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKHRtbV90aXNzdWUgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJlYWQodGlzc3VlX25hbWUsIHRtbSkgJT4lICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpe2xvZzEwKHgrMSl9KSU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoInRhcmdldF9pZCIpICkKCmNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSA8LSBjbGFzc2lmaWNhdGlvbl90aXNzdWUgJT4lIAogIGxlZnRfam9pbih0aXNzdWVfdGF1LCBieSA9IGMoImdlbmUiID0gImdlbmUiKSkgJT4lIAogIG11dGF0ZSh0YXVfc2NvcmUgPSBpZmVsc2Uoc3BlY19jYXRlZ29yeSA9PSAibm90IGRldGVjdGVkIiwgTkEsIHRhdV9zY29yZSkpCgpgYGAKCiNGaWd1cmUgMwojI0ZpZ3VyZSAzQSAtIFBpZSBjaGFydHMgYXQgdGlzc3VlIHR5cGUgbGV2ZWwKYGBge3J9CmdlbmVfY2F0ZWdvcnlfcGFsIDwtIGMoIkRldGVjdGVkIGluIGFsbCIgPSAiIzI1MzQ5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIG1hbnkiID0gIiMyYzdmYjgiLAogICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBzb21lIiA9ICIjNDFiNmM0IiwKICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gc2luZ2xlIiA9ICIjYTFkYWI0IiwKICAgICAgICAgICAgICAgICAgICAgICAiTm90IGRldGVjdGVkICIgPSAiCSNiZWJlYmUiLAogICAgICAgICAgICAgICAgICAgICAgICJMb3cgdGlzc3VlIHNwZWNpZmljaXR5IiA9ICJncmV5NDAiLAogICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5oYW5jZWQiID0gIiM5ODRlYTMiLAogICAgICAgICAgICAgICAgICAgICAgICJHcm91cCBlbnJpY2hlZCIgPSAiI0ZGOUQwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbnJpY2hlZCIgPSAiI2U0MWExYyIpCgpwbG90X2RhdGEgPC0KICBjbGFzc2lmaWNhdGlvbl90aXNzdWUgJT4lIAogIHJlbmFtZShzcGVjaWZpY2l0eSA9IHNwZWNfY2F0ZWdvcnksIGRpc3RyaWJ1dGlvbiA9IGRpc3RfY2F0ZWdvcnkpICU+JSAKICBzZWxlY3QoZ2VuZSwgc3BlY2lmaWNpdHksIGRpc3RyaWJ1dGlvbikgJT4lIAogIGdhdGhlcihjbGFzc190eXBlLCBjbGFzcywgLTEpICU+JSAKICBncm91cF9ieShjbGFzc190eXBlLCBjbGFzcykgJT4lIAogIGNvdW50KCkgJT4lCiAgZ3JvdXBfYnkoY2xhc3NfdHlwZSkgJT4lIAogIG11dGF0ZShwZXJjID0gMTAwICogbiAvIHN1bShuKSwKICAgICAgICAgbGFiZWwgPSBwYXN0ZTAobiwgIlxuIiwgcm91bmQocGVyYywgMSksICIlIikpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShjbGFzcyA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoY2xhc3MpLCBzdHJfdG9fc2VudGVuY2UoYygidGlzc3VlIGVucmljaGVkIiwgImdyb3VwIGVucmljaGVkIiwgICJ0aXNzdWUgZW5oYW5jZWQiLCAibG93IHRpc3N1ZSBzcGVjaWZpY2l0eSIsICJkZXRlY3RlZCBpbiBhbGwiLCAiZGV0ZWN0ZWQgaW4gbWFueSIsICJkZXRlY3RlZCBpbiBzb21lIiwgImRldGVjdGVkIGluIHNpbmdsZSIsICJub3QgZGV0ZWN0ZWQiKSkpLAogICAgICAgICBjbGFzc190eXBlID0gZmFjdG9yKHN0cl90b19zZW50ZW5jZShjbGFzc190eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJTcGVjaWZpY2l0eSIsICJEaXN0cmlidXRpb24iKSkpCiAgICAKcGxvdF9kb2RnZSA9IDAuMQpwbG90X2RhdGEgJT4lIAogIGFycmFuZ2UobWF0Y2goY2xhc3MsIHJldihsZXZlbHMoY2xhc3MpKSkpICU+JSAKICBncm91cF9ieShjbGFzc190eXBlKSAlPiUgCiAgbXV0YXRlKHlfc3RhY2sgPSBjdW1zdW0obikgLSBuLzIpICU+JSAKICB7Z2dwbG90KC4sIGFlcygxLCBuLCBmaWxsID0gY2xhc3MsIGdyb3VwID0gY2xhc3MsIAogICAgICAgICAgICAgICAgIGxhYmVsID0gbGFiZWwpKSArCiAgICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRiwgCiAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgCiAgICAgICAgICAgICAgIHdpZHRoID0gMSkgKyAKICAgICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMS41ICsgcGxvdF9kb2RnZSwgeGVuZCA9IDEuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IHlfc3RhY2ssIHllbmQgPSB5X3N0YWNrKSwgc2l6ZSA9IDAuNSkgKwogICAgICBnZW9tX3RleHRfcmVwZWwoYWVzKHggPSAxLjUgKyBwbG90X2RvZGdlLCB5ID0geV9zdGFjayksIAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBudWRnZV94ID0gcGxvdF9kb2RnZSwgCiAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50LnNpemUgPSAwLjUsIHNpemUgPSAyNC8xMSkgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnZW5lX2NhdGVnb3J5X3BhbCkgKyAKICAgICAgZmFjZXRfd3JhcCh+Y2xhc3NfdHlwZSkgKwogICAgICBjb29yZF9wb2xhcigieSIsc3RhcnQgPSAwKSArCiAgICAgIHRoZW1lX3ZvaWQoKSArIAogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKGMoMCwwLjgpKSl9CgoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL2NsYXNzX3BpZXNfdGlzc3VlX3R5cGUucGRmIix3aWR0aCA9IDYsIGhlaWdodCA9IDUpCgpgYGAKIyNGaWd1cmUgM0IgLSBEaXN0aWJ1dGlvbiBmb3IgZWFjaCB0aXNzdWUgdHlwZQpgYGB7cn0KCmNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSAlPiUgZ3JvdXBfYnkoZGlzdF9jYXRlZ29yeSkgJT4lIGNvdW50KCkKCm9yZGVyZWRfbmFtZXNfZGlzdHIgPC0gY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAKICBmaWx0ZXIoZGlzdF9jYXRlZ29yeSAlaW4lIGMoImRldGVjdGVkIGluIHNpbmdsZSIsICJkZXRlY3RlZCBpbiBzb21lIiwgImRldGVjdGVkIGluIG1hbnkiLCAiZGV0ZWN0ZWQgaW4gYWxsIikpICU+JSAKICBzZXBhcmF0ZV9yb3dzKHRpc3N1ZXNfZGV0ZWN0ZWQsIHNlcCA9ICI7IikgJT4lIAogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JSAKICBjb3VudCgpICU+JSAKICBncm91cF9ieSh0aXNzdWVzX2RldGVjdGVkKSAlPiUgCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShuKSkgJT4lIAogIGFycmFuZ2Uoc3VtKSAlPiUgCiAgLiR0aXNzdWVzX2RldGVjdGVkICU+JSAKICBzdHJfdG9fc2VudGVuY2UoKQoKZGV0ZWN0aW9uX3BhbGV0dGUgPC0gYygiRGV0ZWN0ZWQgaW4gYWxsIiA9ICIjMjUzNDk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIG1hbnkiID0gIiMyYzdmYjgiLAogICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gc29tZSIgPSAiIzQxYjZjNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBzaW5nbGUiID0gIiNhMWRhYjQiLAogICAgICAgICAgICAgICAgICAgICAgICAiTm90IGRldGVjdGVkICIgPSAiZ3JleSIpCgoKY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JQogIGZpbHRlcigKICAgIGRpc3RfY2F0ZWdvcnkgJWluJSBjKAogICAgICAiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwKICAgICAgImRldGVjdGVkIGluIHNvbWUiLAogICAgICAiZGV0ZWN0ZWQgaW4gbWFueSIsCiAgICAgICJkZXRlY3RlZCBpbiBhbGwiCiAgICApCiAgKSAlPiUKICBzZXBhcmF0ZV9yb3dzKHRpc3N1ZXNfZGV0ZWN0ZWQsIHNlcCA9ICI7IikgJT4lCiAgZ3JvdXBfYnkoZGlzdF9jYXRlZ29yeSwgdGlzc3Vlc19kZXRlY3RlZCkgJT4lCiAgY291bnQoKSAlPiUKICBtdXRhdGUodGlzc3Vlc19kZXRlY3RlZCA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoIHRpc3N1ZXNfZGV0ZWN0ZWQpLCBvcmRlcmVkX25hbWVzX2Rpc3RyKSkgJT4lCiAgbXV0YXRlKGRpc3RfY2F0ZWdvcnkgPSBmYWN0b3IoCiAgICBzdHJfdG9fc2VudGVuY2UoIGRpc3RfY2F0ZWdvcnkpLAogICAgYygKICAgICAgIkRldGVjdGVkIGluIHNpbmdsZSIsCiAgICAgICJEZXRlY3RlZCBpbiBzb21lIiwKICAgICAgIkRldGVjdGVkIGluIG1hbnkiLAogICAgICAiRGV0ZWN0ZWQgaW4gYWxsIgogICAgKQogICkpICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSB0aXNzdWVzX2RldGVjdGVkLCBmaWxsID0gZGlzdF9jYXRlZ29yeSkpICsKICBnZW9tX2NvbCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBkZXRlY3Rpb25fcGFsZXR0ZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IDAsIGFkZCA9IDApKSArCiAgZ2d0aXRsZSgiTnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzIHBlciB0aXNzdWUgdHlwZSIpICArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIsIGJ5cm93ID0gVFJVRSkpCgogZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL3Rpc3N1ZV9kZXRlY3Rpb25fYS5wZGYiLCBoZWlnaHQgPSAxMS41LCB3aWR0aCA9IDQuNSkKCgpgYGAKCiMjRmlndXJlIDNDIC0gRGlzdHJpYnV0aW9uIGFuZCBUYXUgc2NvcmUKYGBge3J9CmRldGVjdGlvbl9wYWxldHRlIDwtIGMoIkRldGVjdGVkIGluIGFsbCIgPSAiIzI1MzQ5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBtYW55IiA9ICIjMmM3ZmI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIHNvbWUiID0gIiM0MWI2YzQiLAogICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gc2luZ2xlIiA9ICIjYTFkYWI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIk5vdCBkZXRlY3RlZCAiID0gImdyZXkiKQoKcDEgPC0gY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAKICBtdXRhdGUoZGlzdF9jYXRlZ29yeSA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoIGRpc3RfY2F0ZWdvcnkpLCBsZXZlbHMgPSByZXYobmFtZXMoZGV0ZWN0aW9uX3BhbGV0dGUpKSksCiAgICAgICAgIGVucmljaGVkX3Rpc3N1ZXMgPSBzdHJfdG9fc2VudGVuY2UoZW5yaWNoZWRfdGlzc3VlcykpICU+JSAKICBmaWx0ZXIoZGlzdF9jYXRlZ29yeSAhPSAiTm90IGRldGVjdGVkIikgJT4lIAogIGdncGxvdChhZXMoeCA9IHRhdV9zY29yZSwgeSA9IGRpc3RfY2F0ZWdvcnksIGZpbGwgPSBkaXN0X2NhdGVnb3J5KSkgKwogIGdlb21fdmlvbGluKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdlbmVfY2F0ZWdvcnlfcGFsLCBuYW1lID0gIlNwZWNpZmljaXR5IikgKwogIHhsYWIoIlRhdSBzY29yZSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgKSAKCiNnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY2xhc3NpZmljYXRpb24vdGF1X3RvX2Rpc3RyaWJ1dGlvbi5wZGYiLHdpZHRoID0gNS41LCBoZWlnaHQgPSA0KQoKCnAyIDwtIGNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSAlPiUKICBnZ3Bsb3QoYWVzKHRhdV9zY29yZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB5bGFiKCJDb3VudCIpKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoImdyYXk5MCIpLAogICAgICAgICNheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpCgpwMi8gcDEgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAzKSkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9kaXN0cmlidXRpb25fYW5kX2RlbmRyb2dyYW1fdGlzc3VlX3dfb3ZlcmFsbC5wZGYiLHdpZHRoID0gNi41LCBoZWlnaHQgPSA2KQpgYGAKIyMjRXh0cmEgRmlndXJlcyBub3QgaW4gcmVwb3J0CmBgYHtyfQpkZXRlY3Rpb25fcGFsZXR0ZSA8LSBjKCJkZXRlY3RlZCBpbiBhbGwiID0gIiMyNTM0OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAiZGV0ZWN0ZWQgaW4gbWFueSIgPSAiIzJjN2ZiOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJkZXRlY3RlZCBpbiBzb21lIiA9ICIjNDFiNmM0IiwKICAgICAgICAgICAgICAgICAgICAgICAgImRldGVjdGVkIGluIHNpbmdsZSIgPSAiI2ExZGFiNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJub3QgZGV0ZWN0ZWQgIiA9ICJncmV5IikKCm9yZGVyZWRfbmFtZXNfZGlzdHIgPC0gY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAgCiAgZmlsdGVyKGRpc3RfY2F0ZWdvcnkgJWluJSBjKCJkZXRlY3RlZCBpbiBzaW5nbGUiIywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICJkZXRlY3RlZCBpbiBzb21lIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSAlPiUgCiAgc2VwYXJhdGVfcm93cyh0aXNzdWVzX2RldGVjdGVkLCBzZXAgPSAiOyIpICU+JSAKICBncm91cF9ieShkaXN0X2NhdGVnb3J5LCB0aXNzdWVzX2RldGVjdGVkKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgZ3JvdXBfYnkodGlzc3Vlc19kZXRlY3RlZCkgJT4lIAogIHN1bW1hcmlzZShzdW0gPSBzdW0obikpICU+JSAKICBhcnJhbmdlKHN1bSkgJT4lIAogIC4kdGlzc3Vlc19kZXRlY3RlZAoKY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAgCiAgZmlsdGVyKAogICAgZGlzdF9jYXRlZ29yeSAlaW4lIGMoCiAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiIywKIwogICAgKQogICkgJT4lCiAgc2VwYXJhdGVfcm93cyh0aXNzdWVzX2RldGVjdGVkLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JQogIGNvdW50KCkgJT4lCiAgbXV0YXRlKHRpc3N1ZXNfZGV0ZWN0ZWQgPSBmYWN0b3IodGlzc3Vlc19kZXRlY3RlZCwgb3JkZXJlZF9uYW1lc19kaXN0cikpICU+JQogIG11dGF0ZShkaXN0X2NhdGVnb3J5ID0gZmFjdG9yKAogICAgZGlzdF9jYXRlZ29yeSwKICAgIGMoCiAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiLAogICAgICAiZGV0ZWN0ZWQgaW4gc29tZSIsCiAgICAgICJkZXRlY3RlZCBpbiBtYW55IiwKICAgICAgImRldGVjdGVkIGluIGFsbCIKICAgICkKICApKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gdGlzc3Vlc19kZXRlY3RlZCwgZmlsbCA9IGRpc3RfY2F0ZWdvcnkpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZGV0ZWN0aW9uX3BhbGV0dGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZSgKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLCBhZGQgPSAwKSkgKwojICBnZ3RpdGxlKCJOdW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgcGVyIHRpc3N1ZSB0eXBlIikgICsKICBnZ3RpdGxlKCJOdW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgZGV0ZWN0ZWQgaW4gYSBzaW5nbGUgdGlzc3VlIikgICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMiwgYnlyb3cgPSBUUlVFKSkKCmBgYApgYGB7cn0KZGV0ZWN0aW9uX3BhbGV0dGUgPC0gYygiZGV0ZWN0ZWQgaW4gYWxsIiA9ICIjMjUzNDk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgImRldGVjdGVkIGluIG1hbnkiID0gIiMyYzdmYjgiLAogICAgICAgICAgICAgICAgICAgICAgICAiZGV0ZWN0ZWQgaW4gc29tZSIgPSAiIzQxYjZjNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiID0gIiNhMWRhYjQiLAogICAgICAgICAgICAgICAgICAgICAgICAibm90IGRldGVjdGVkICIgPSAiZ3JleSIpCgpvcmRlcmVkX25hbWVzX2Rpc3RyIDwtIGNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSAlPiUgIAogIGZpbHRlcihkaXN0X2NhdGVnb3J5ICVpbiUgYygiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRldGVjdGVkIGluIHNvbWUiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICU+JSAKICBzZXBhcmF0ZV9yb3dzKHRpc3N1ZXNfZGV0ZWN0ZWQsIHNlcCA9ICI7IikgJT4lIAogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JSAKICBjb3VudCgpICU+JSAKICBncm91cF9ieSh0aXNzdWVzX2RldGVjdGVkKSAlPiUgCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShuKSkgJT4lIAogIGFycmFuZ2Uoc3VtKSAlPiUgCiAgLiR0aXNzdWVzX2RldGVjdGVkCgpjbGFzc2lmaWNhdGlvbl90aXNzdWUgJT4lICAKICBmaWx0ZXIoCiAgICBkaXN0X2NhdGVnb3J5ICVpbiUgYygKICAgICAgImRldGVjdGVkIGluIHNpbmdsZSIsCiAgICAgICJkZXRlY3RlZCBpbiBzb21lIgogICAgKQogICkgJT4lCiAgc2VwYXJhdGVfcm93cyh0aXNzdWVzX2RldGVjdGVkLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JQogIGNvdW50KCkgJT4lCiAgbXV0YXRlKHRpc3N1ZXNfZGV0ZWN0ZWQgPSBmYWN0b3IodGlzc3Vlc19kZXRlY3RlZCwgb3JkZXJlZF9uYW1lc19kaXN0cikpICU+JQogIG11dGF0ZShkaXN0X2NhdGVnb3J5ID0gZmFjdG9yKAogICAgZGlzdF9jYXRlZ29yeSwKICAgIGMoCiAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiLAogICAgICAiZGV0ZWN0ZWQgaW4gc29tZSIsCiAgICAgICJkZXRlY3RlZCBpbiBtYW55IiwKICAgICAgImRldGVjdGVkIGluIGFsbCIKICAgICkKICApKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gdGlzc3Vlc19kZXRlY3RlZCwgZmlsbCA9IGRpc3RfY2F0ZWdvcnkpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZGV0ZWN0aW9uX3BhbGV0dGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZSgKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLCBhZGQgPSAwKSkgKwogIGdndGl0bGUoIk51bWJlciBvZiBkZXRlY3RlZCBnZW5lcyBwZXIgdGlzc3VlIHR5cGUiKSAgKwogIyBnZ3RpdGxlKCJOdW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgZGV0ZWN0ZWQgaW4gYSBzaW5nbGUgdGlzc3VlIikgICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMiwgYnlyb3cgPSBUUlVFKSkKCmBgYAoKI0ZpZ3VyZSA0CiMjRmlndXJlIDRBIC0gUGllIGNoYXJ0cyBhdCBncm91cGVkIHRpc3N1ZSBsZXZlbApgYGB7cn0KZ2VuZV9jYXRlZ29yeV9wYWwgPC0gYygiRGV0ZWN0ZWQgaW4gYWxsIiA9ICIjMjUzNDk0IiwKICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gbWFueSIgPSAiIzJjN2ZiOCIsCiAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIHNvbWUiID0gIiM0MWI2YzQiLAogICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBzaW5nbGUiID0gIiNhMWRhYjQiLAogICAgICAgICAgICAgICAgICAgICAgICJOb3QgZGV0ZWN0ZWQgIiA9ICIJI2JlYmViZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkxvdyB0aXNzdWUgc3BlY2lmaWNpdHkiID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbmhhbmNlZCIgPSAiIzk4NGVhMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwIGVucmljaGVkIiA9ICIjRkY5RDAwIiwKICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIGVucmljaGVkIiA9ICIjZTQxYTFjIikKCnBsb3RfZGF0YSA8LQogIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUgCiAgcmVuYW1lKHNwZWNpZmljaXR5ID0gc3BlY19jYXRlZ29yeSwgZGlzdHJpYnV0aW9uID0gZGlzdF9jYXRlZ29yeSkgJT4lIAogIHNlbGVjdChnZW5lLCBzcGVjaWZpY2l0eSwgZGlzdHJpYnV0aW9uKSAlPiUgCiAgZ2F0aGVyKGNsYXNzX3R5cGUsIGNsYXNzLCAtMSkgJT4lIAogIGdyb3VwX2J5KGNsYXNzX3R5cGUsIGNsYXNzKSAlPiUgCiAgY291bnQoKSAlPiUKICBncm91cF9ieShjbGFzc190eXBlKSAlPiUgCiAgbXV0YXRlKHBlcmMgPSAxMDAgKiBuIC8gc3VtKG4pLAogICAgICAgICBsYWJlbCA9IHBhc3RlMChuLCAiXG4iLCByb3VuZChwZXJjLCAxKSwgIiUiKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGNsYXNzID0gZmFjdG9yKHN0cl90b19zZW50ZW5jZShjbGFzcyksIHN0cl90b19zZW50ZW5jZShjKCJ0aXNzdWUgZW5yaWNoZWQiLCAiZ3JvdXAgZW5yaWNoZWQiLCAgInRpc3N1ZSBlbmhhbmNlZCIsICJsb3cgdGlzc3VlIHNwZWNpZmljaXR5IiwgImRldGVjdGVkIGluIGFsbCIsICJkZXRlY3RlZCBpbiBtYW55IiwgImRldGVjdGVkIGluIHNvbWUiLCAiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwgIm5vdCBkZXRlY3RlZCIpKSksCiAgICAgICAgIGNsYXNzX3R5cGUgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKGNsYXNzX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIlNwZWNpZmljaXR5IiwgIkRpc3RyaWJ1dGlvbiIpKSkKICAgIApwbG90X2RvZGdlID0gMC4xCnBsb3RfZGF0YSAlPiUgCiAgYXJyYW5nZShtYXRjaChjbGFzcywgcmV2KGxldmVscyhjbGFzcykpKSkgJT4lIAogIGdyb3VwX2J5KGNsYXNzX3R5cGUpICU+JSAKICBtdXRhdGUoeV9zdGFjayA9IGN1bXN1bShuKSAtIG4vMikgJT4lIAogIHtnZ3Bsb3QoLiwgYWVzKDEsIG4sIGZpbGwgPSBjbGFzcywgZ3JvdXAgPSBjbGFzcywgCiAgICAgICAgICAgICAgICAgbGFiZWwgPSBsYWJlbCkpICsKICAgICAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGLCAKICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCAKICAgICAgICAgICAgICAgd2lkdGggPSAxKSArIAogICAgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxLjUgKyBwbG90X2RvZGdlLCB4ZW5kID0gMS41LCAKICAgICAgICAgICAgICAgICAgICAgICB5ID0geV9zdGFjaywgeWVuZCA9IHlfc3RhY2spLCBzaXplID0gMC41KSArCiAgICAgIGdlb21fdGV4dF9yZXBlbChhZXMoeCA9IDEuNSArIHBsb3RfZG9kZ2UsIHkgPSB5X3N0YWNrKSwgCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIG51ZGdlX3ggPSBwbG90X2RvZGdlLCAKICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuNSwgc2l6ZSA9IDI0LzExKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdlbmVfY2F0ZWdvcnlfcGFsKSArIAogICAgICBmYWNldF93cmFwKH5jbGFzc190eXBlKSArCiAgICAgIGNvb3JkX3BvbGFyKCJ5IixzdGFydCA9IDApICsKICAgICAgdGhlbWVfdm9pZCgpICsgCiAgICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oYygwLDAuOCkpKX0KCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY2xhc3NpZmljYXRpb24vY2xhc3NfcGllc19ncm91cGVkX3Rpc3N1ZS5wZGYiLHdpZHRoID0gNiwgaGVpZ2h0ID0gNSkKYGBgCgojI0ZpZ3VyZSA0QiAtIFNwZWNlZmljaXR5IGZvciBlYWNoIGdyb3VwZWQgdGlzc3VlCmBgYHtyfQpjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lIGdyb3VwX2J5KHNwZWNfY2F0ZWdvcnkpICU+JSBjb3VudCgpIAoKb3JkZXJlZF9uYW1lc19zcCA8LQogIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBmaWx0ZXIoc3BlY19jYXRlZ29yeSAlaW4lIGMoImdyb3VwIGVucmljaGVkIiwgInRpc3N1ZSBlbnJpY2hlZCIsICJ0aXNzdWUgZW5oYW5jZWQiKSkgJT4lCiAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZF90aXNzdWVzLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KHNwZWNfY2F0ZWdvcnksIGVucmljaGVkX3Rpc3N1ZXMpICU+JQogIGNvdW50KCkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShlbnJpY2hlZF90aXNzdWVzKSAlPiUKICBzdW1tYXJpc2Uoc3VtID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2Uoc3VtKSAlPiUKICAuJGVucmljaGVkX3Rpc3N1ZXMgJT4lIAogIHN0cl90b19zZW50ZW5jZSgpCiAgCiAgCiAgCgpzcGVjaWZpY2l0eV9wYWxldHRlIDwtIHJldihjKCJOb3QgZGV0ZWN0ZWQiID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTG93IHRpc3N1ZSBzcGVjaWZpY2l0eSIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbmhhbmNlZCIgPSAiIzk4NGVhMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cCBlbnJpY2hlZCIgPSAiI0ZGOUQwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5yaWNoZWQiID0gIiNlNDFhMWMiKSkKCmNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBmaWx0ZXIoc3BlY19jYXRlZ29yeSAlaW4lIGMoImdyb3VwIGVucmljaGVkIiwgInRpc3N1ZSBlbnJpY2hlZCIsICJ0aXNzdWUgZW5oYW5jZWQiKSkgJT4lCiAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZF90aXNzdWVzLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KHNwZWNfY2F0ZWdvcnksIGVucmljaGVkX3Rpc3N1ZXMpICU+JQogIGNvdW50KCkgJT4lCiAgbXV0YXRlKGVucmljaGVkX3Rpc3N1ZXMgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKGVucmljaGVkX3Rpc3N1ZXMpLCBvcmRlcmVkX25hbWVzX3NwKSkgJT4lCiAgbXV0YXRlKHNwZWNfY2F0ZWdvcnkgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKHNwZWNfY2F0ZWdvcnkpLCBjKCJUaXNzdWUgZW5yaWNoZWQiLCAiR3JvdXAgZW5yaWNoZWQiLCAgIlRpc3N1ZSBlbmhhbmNlZCIpKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbiwgeSA9IGVucmljaGVkX3Rpc3N1ZXMsIGZpbGwgPSBzcGVjX2NhdGVnb3J5KSkgKwogIGdlb21fY29sKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHNwZWNpZmljaXR5X3BhbGV0dGUpICsKICB4bGFiKCJOdW1iZXIgb2YgZ2VuZXMiKSsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZSgKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpCiAgKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpICsKICBnZ3RpdGxlKCJTcGVjaWZpY2l0eSBjYXRlZ29yeSBwZXIgY29uc2Vuc3VzIHRpc3N1ZSIpIAoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL2NvbnNlbnN1c190aXNzdWVfc3BlY2lmaWNpdHkucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSA1LjUpCmBgYAoKCiMjRmlndXJlIDRDIC0gU3BlY2lmaWNpdHkgYW5kIFRhdSBzY29yZQpgYGB7cn0KCgpzcGVjaWZpY2l0eV9wYWxldHRlIDwtIHJldihjKCJOb3QgZGV0ZWN0ZWQiID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTG93IHRpc3N1ZSBzcGVjaWZpY2l0eSIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbmhhbmNlZCIgPSAiIzk4NGVhMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cCBlbnJpY2hlZCIgPSAiI0ZGOUQwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5yaWNoZWQiID0gIiNlNDFhMWMiKSkKCnAxIDwtIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUgCiAgbXV0YXRlKHNwZWNfY2F0ZWdvcnkgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKCBzcGVjX2NhdGVnb3J5KSwgbGV2ZWxzID0gbmFtZXMoc3BlY2lmaWNpdHlfcGFsZXR0ZSkpLAogICAgICAgICBlbnJpY2hlZF90aXNzdWVzID0gc3RyX3RvX3NlbnRlbmNlKGVucmljaGVkX3Rpc3N1ZXMpKSAlPiUgCiAgZmlsdGVyKHNwZWNfY2F0ZWdvcnkgIT0gIk5vdCBkZXRlY3RlZCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB0YXVfc2NvcmUsIHkgPSBzcGVjX2NhdGVnb3J5LCBmaWxsID0gc3BlY19jYXRlZ29yeSkpICsKICBnZW9tX3Zpb2xpbigpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnZW5lX2NhdGVnb3J5X3BhbCwgbmFtZSA9ICJTcGVjaWZpY2l0eSIpICsKICB4bGFiKCJUYXUgc2NvcmUiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgCgojZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL3RhdV90b19zcGVjaWZpY2l0eS5wZGYiLHdpZHRoID0gNS41LCBoZWlnaHQgPSA0KQoKCnAyIDwtIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBnZ3Bsb3QoYWVzKHRhdV9zY29yZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB5bGFiKCJDb3VudCIpKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoImdyYXk5MCIpLAogICAgICAgICNheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpCgpwMi8gcDEgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAzKSkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9zcGVjaWZpY2l0eV9hbmRfZGVuZHJvZ3JhbV9jb25zZW5zdXNfd19vdmVyYWxsLnBkZiIsd2lkdGggPSA3LCBoZWlnaHQgPSA2KQoKYGBgCgojI0ZpZ3VyZSA0RCAtIEdlbmUgYW5ub3RhdGlvbiBhbGx1dmlhbCBwbG90CmBgYHtyfQp3aWR0aCA9IDAuMQoKYWxsdXZfMSA8LQogIAogIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBtdXRhdGUoU3BlY2lmaWNpdHkgPSBzdHJfdG9fc2VudGVuY2Uoc3BlY19jYXRlZ29yeSksCiAgICAgICAgICBEaXN0cmlidXRpb24gPSBzdHJfdG9fc2VudGVuY2UoZGlzdF9jYXRlZ29yeSkpICU+JQogIHNlbGVjdChTcGVjaWZpY2l0eSwKICAgICAgICAgRGlzdHJpYnV0aW9uKSAlPiUKICBtdXRhdGUocm93X24gPSByb3dfbnVtYmVyKCkpICU+JQogIGdhdGhlcihiYXIsIGNodW5rLCAtcm93X24pICU+JQogIG11dGF0ZShjb2xvcl92YXJzID0gMSkgJT4lCiAgZ3JvdXBfYnkocm93X24pICU+JQogIG11dGF0ZShjaHVua19jb2xvciA9IGNodW5rW21hdGNoKGMoIlNwZWNpZmljaXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaXN0cmlidXRpb24iKVtjb2xvcl92YXJzXSwgYmFyKV0pICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgCiAgCiAgbXV0YXRlKGNodW5rID0gZmFjdG9yKGNodW5rLCBsZXZlbHMgPSBjKCdUaXNzdWUgZW5yaWNoZWQnLCAnR3JvdXAgZW5yaWNoZWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1Rpc3N1ZSBlbmhhbmNlZCcsICdMb3cgdGlzc3VlIHNwZWNpZmljaXR5JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdEZXRlY3RlZCBpbiBzaW5nbGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnRGV0ZWN0ZWQgaW4gc29tZScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnRGV0ZWN0ZWQgaW4gbWFueScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnRGV0ZWN0ZWQgaW4gYWxsJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ05vdCBkZXRlY3RlZCcpKSwKICAgICAgICAgYmFyID0gZmFjdG9yKGJhciwgbGV2ZWxzID0gYygiU3BlY2lmaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaXN0cmlidXRpb24iKSkpICU+JQogIAogIAogIGdncGxvdChhZXMoeCA9IGJhciwgc3RyYXR1bSA9IGNodW5rLCBhbGx1dml1bSA9IHJvd19uLAogICAgICAgICAgICAgeSA9IDEpKSArCiAgCiAgZ2VvbV9mbG93KGFlcyhmaWxsID0gY2h1bmtfY29sb3IpLCAKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCB3aWR0aCA9IHdpZHRoLAogICAgICAgICAgICBrbm90LnBvcyA9IDEvNikgKwogIGdlb21fc3RyYXR1bShhZXMoZmlsbCA9IGNodW5rKSwgCiAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRiwgY29sb3IgPSBOQSwgd2lkdGggPSB3aWR0aCkgKwogIAogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gYyguMSwgLjEpLCBwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhnZW5lX2NhdGVnb3J5X3BhbCkpICsgCiAgCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGNvb3JkX2ZsaXAoKQoKIyBhbGx1dl8xCgpmbG93X2RhdGEgPC0KICBnZ3Bsb3RfYnVpbGQoYWxsdXZfMSkkZGF0YVtbMV1dICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHsKICAgIGlmICgic2lkZSIgJWluJSBuYW1lcyguKSkgewogICAgICAuCiAgICB9IGVsc2V7CiAgICAgIG11dGF0ZSguLAogICAgICAgICAgICAgc2lkZSA9IGNhc2Vfd2hlbihmbG93ID09ICJmcm9tIiB+ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsb3cgPT0gInRvIiB+ICJlbmQiKSkKICAgIH0KICB9CgoKCnN0cmF0dW1fZGF0YSA8LSAKICBnZ3Bsb3RfYnVpbGQoYWxsdXZfMSkkZGF0YVtbMl1dCgpmbG93X2RhdGFfbGFiZWxzIDwtCiAgZmxvd19kYXRhICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHNlbGVjdCh4LCBzdHJhdHVtLCBncm91cCwgc2lkZSwgeW1pbiwgeW1heCkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNpZGUsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBjKHgsIHN0cmF0dW0sIHltaW4sIHltYXgpKSAlPiUKICBtdXRhdGVfYXQoCiAgICBjKAogICAgICAieF9lbmQiLAogICAgICAieW1heF9lbmQiLAogICAgICAieW1pbl9lbmQiLAogICAgICAieF9zdGFydCIsCiAgICAgICJ5bWF4X3N0YXJ0IiwKICAgICAgInltaW5fc3RhcnQiCiAgICApLAogICAgYXMubnVtZXJpYwogICkgJT4lCiAgZ3JvdXBfYnkoc3RyYXR1bV9zdGFydCwgc3RyYXR1bV9lbmQsIHhfc3RhcnQsIHhfZW5kKSAlPiUKICBzdW1tYXJpc2UoCiAgICB5X2VuZCA9IChtaW4oeW1pbl9lbmQpICsgbWF4KHltYXhfZW5kKSkgLyAyLAogICAgeV9zdGFydCA9IChtaW4oeW1pbl9zdGFydCkgKyBtYXgoeW1heF9zdGFydCkpIC8gMiwKICAgIHNpemUgPSBtYXgoeW1heF9zdGFydCkgLSBtaW4oeW1pbl9zdGFydCkKICApCgphbGx1dl8yIDwtIAogIGFsbHV2XzEgKwogIGdlb21fdGV4dChkYXRhID0gZmxvd19kYXRhX2xhYmVscywKICAgICAgICAgICAgYWVzKHggPSB4X3N0YXJ0ICsgd2lkdGgvMiwKICAgICAgICAgICAgICAgIHkgPSB5X3N0YXJ0LCAKICAgICAgICAgICAgICAgIGxhYmVsID0gc2l6ZSksIAogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEYsIAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgYW5nbGUgPSAtOTAsCiAgICAgICAgICAgIGhqdXN0ID0gMSwKICAgICAgICAgICAgdmp1c3QgPSAwLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZsb3dfZGF0YV9sYWJlbHMsCiAgICAgICAgICAgIGFlcyh4ID0geF9lbmQgLSB3aWR0aC8yLAogICAgICAgICAgICAgICAgeSA9IHlfZW5kLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gc2l6ZSksIAogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEYsIAogICAgICAgICAgICBzaXplID0gMywgCiAgICAgICAgICAgIGFuZ2xlID0gLTkwLAogICAgICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgICAgIHZqdXN0ID0gMC41KSArCiAgCiAgIyBTdHJhdHVtIGxhYmVsCiAgCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdHJhdHVtX2RhdGEgJT4lCiAgICAgICAgICAgICAgZmlsdGVyKHggPT0gMSksCiAgICAgICAgICAgIGFlcyh4ID0geCAtIHdpZHRoLzIsCiAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgIGxhYmVsID0gc3RyYXR1bSksCiAgICAgICAgICAgIHNpemUgPSA0LCAKICAgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRikgKyAKICBnZW9tX3RleHQoZGF0YSA9IHN0cmF0dW1fZGF0YSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeCA9PSAyKSwKICAgICAgICAgICAgYWVzKHggPSB4ICsgd2lkdGgvMiwKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgbGFiZWwgPSBzdHJhdHVtKSwKICAgICAgICAgICAgc2l6ZSA9IDQsIAogICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRikgKyAKICAKICBnZW9tX3RleHQoZGF0YSA9IHN0cmF0dW1fZGF0YSwKICAgICAgICAgICAgYWVzKHggPSB4LCAKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgbGFiZWwgPSB5bWF4IC0geW1pbiksIAogICAgICAgICAgICBzaXplID0gNCwgCiAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRikKCgphbGx1dl8yCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9hbGx1dmlhbF9jbGFzc2lmaWNhdGlvbi5wZGYiLHdpZHRoID0gOCwgaGVpZ2h0ID0gMykKCmBgYAojIyNFeHRyYSBmaWd1cmVzIG5vdCBpbiByZXBvcnQKYGBge3J9Cm9yZ2FuX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KGNvbnNlbnN1c190aXNzdWVfbmFtZSwgY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcikgJT4lIHVuaXF1ZSgpCnBhbCA8LSAgb3JnYW5fY29sb3JzJGNvbnNlbnN1c190aXNzdWVfY29sb3IKcGFsIDwtIHNldE5hbWVzKHBhbCwgc3RyX3RvX3NlbnRlbmNlKG9yZ2FuX2NvbG9ycyRjb25zZW5zdXNfdGlzc3VlX25hbWUpKQoKCnNwZWNpZmljaXR5X3BhbGV0dGUgPC0gcmV2KGMoIk5vdCBkZXRlY3RlZCIgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb3cgdGlzc3VlIHNwZWNpZmljaXR5IiA9ICJncmV5NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIGVuaGFuY2VkIiA9ICIjOTg0ZWEzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwIGVucmljaGVkIiA9ICIjRkY5RDAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbnJpY2hlZCIgPSAiI2U0MWExYyIpKQoKCmNsYXNzX3RhYmxlX3RlbXAgPC0KICBjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lCiAgc2VsZWN0KGdlbmUsIHNwZWNfY2F0ZWdvcnksIGVucmljaGVkX3Rpc3N1ZXMpICU+JQogIHNlcGFyYXRlX3Jvd3MoZW5yaWNoZWRfdGlzc3Vlcywgc2VwID0gIjsiKSAlPiUKICBtdXRhdGUoc3BlY19jYXRlZ29yeSA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoIHNwZWNfY2F0ZWdvcnkpLCBsZXZlbHMgPSBuYW1lcyhzcGVjaWZpY2l0eV9wYWxldHRlKSksCiAgICAgICAgIGVucmljaGVkX3Rpc3N1ZXMgPSBzdHJfdG9fc2VudGVuY2UoZW5yaWNoZWRfdGlzc3VlcykpCgpwbG90X2RlbmRybyA8LQogIHRtbV9jb25zZW5zdXNfdGlzc3VlICU+JSAKICBzZWxlY3QodGFyZ2V0X2lkLCBjb25zZW5zdXNfdGlzc3VlX25hbWUsIHRtbSkgJT4lIAogIG11dGF0ZShjb25zZW5zdXNfdGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UoY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSkgJT4lCiAgc3ByZWFkKHRhcmdldF9pZCwgdG1tKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImNvbnNlbnN1c190aXNzdWVfbmFtZSIpICAlPiUKICB0KCkgJT4lCiAgY29yKG1ldGhvZCA9ICJzcGVhcm1hbiIpICU+JQogIAogIHsxIC0gLn0gJT4lCiAgYXMuZGlzdCgpICU+JSAKICBoY2x1c3QobWV0aG9kID0gImF2ZXJhZ2UiKSAlVD4lCiAgcGxvdCAlPiUKICBkZW5kcm9fZGF0YSgpCiAgCiAgCmRlbmRyb19wbG90X2RhdGEgPC0gCiAgbGVmdF9qb2luKHBsb3RfZGVuZHJvJHNlZ21lbnRzLCAKICAgICAgICAgICAgcGxvdF9kZW5kcm8kbGFiZWxzLCAKICAgICAgICAgICAgYnkgPSBjKCJ4IiA9ICJ4IiwgInllbmQiID0gInkiKSkgCgpsZWZ0X3Bsb3QgPC0gCiAgZGVuZHJvX3Bsb3RfZGF0YSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4PXksIHk9eCwgeGVuZD15ZW5kLCB5ZW5kPXhlbmQsIGdyb3VwID0gbGFiZWwpKSsKICBnZW9tX3JlY3QoYWVzKHhtaW49MCwgeW1pbj14ICsgMC41LCAKICAgICAgICAgICAgICAgIHhtYXg9LTAuMDIsIHltYXg9eGVuZCAtIDAuNSwgCiAgICAgICAgICAgICAgICBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSsKICBzY2FsZV94X3JldmVyc2UoZXhwYW5kID0gZXhwYW5zaW9uKDApLCBwb3NpdGlvbiA9ICJ0b3AiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKCkpICsKICB4bGFiKCIxIC0gU3BlYXJtYW4ncyByaG8iKSArCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCB1bml0cyA9ICJtbSIpLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSAKCnJpZ2h0X3Bsb3QgPC0gCiAgY2xhc3NfdGFibGVfdGVtcCAlPiUKICBmaWx0ZXIoIWlzLm5hKGVucmljaGVkX3Rpc3N1ZXMpKSAlPiUKICBncm91cF9ieShlbnJpY2hlZF90aXNzdWVzLCBzcGVjX2NhdGVnb3J5KSAlPiUKICBzdW1tYXJpc2Uobl9nZW5lcyA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShlbnJpY2hlZF90aXNzdWVzID0gZmFjdG9yKGVucmljaGVkX3Rpc3N1ZXMsIGxldmVscyA9IHBsb3RfZGVuZHJvJGxhYmVscyRsYWJlbCksCiAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPSBmYWN0b3Ioc3BlY19jYXRlZ29yeSwgbmFtZXMoc3BlY2lmaWNpdHlfcGFsZXR0ZSkpKSAlPiUKICBnZ3Bsb3QoYWVzKG5fZ2VuZXMsIGVucmljaGVkX3Rpc3N1ZXMsIGZpbGwgPSBzcGVjX2NhdGVnb3J5KSkgKwogIGdlb21fY29sKHdpZHRoID0gMC44LCBzaXplID0gMC4xKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnZW5lX2NhdGVnb3J5X3BhbCwgbmFtZSA9ICJTcGVjaWZpY2l0eSIpICsKICAjIGNvb3JkX2ZsaXAoKSArCiAgeGxhYigiTnVtYmVyIG9mIGdlbmVzIikgKwogIAogIHNjYWxlX3hfY29udGludW91cyhwb3NpdGlvbiA9ICJ0b3AiLCBleHBhbmQgPSBleHBhbnNpb24oYygwLCAwLjEpKSkgKyAKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigpKSArCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCAKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNywgMC41KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQoKbGVmdF9wbG90ICsgcmlnaHRfcGxvdCArCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygwLjMsIDEpKQoKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9zcGVjaWZpY2l0eV9hbmRfZGVuZHJvZ3JhbV9jb25zZW5zdXMucGRmIix3aWR0aCA9IDcsIGhlaWdodCA9IDcpCgpgYGAKI0ZpZ3VyZSA1CmBgYHtyfQojI1Bsb3QgYmFzZWQgb24gTWF4IEthcmxzc29uJ3MgbmV0d29yayBwbG90CgpjbGFzc2lmaWNhdGlvbl9uZXR3b3JrX3Bsb3RfMiA8LSAKICBmdW5jdGlvbihjbGFzc190YWJsZSwgZ2VuZV9jb2wsIHNwZWNfY29sLCBlbnJpY2hlZF9jb2wsIHNwZWNfZmlsdGVyLCBwYWwsIHNhdmVuYW1lLCBlbnJpY2hlZF9zZXAgPSAiOyIsIAogICAgICAgICAgIG5vZGVfZmlsdGVyX3JhbmsgPSAyLCBub2RlX2ZpbHRlcl9zaG93X2NhdCA9IGMoInRpc3N1ZSBlbnJpY2hlZCIpLCAKICAgICAgICAgICBub2RlX2ZpbHRlcl9taW4gPSAyLCBub2RlX2ZpbHRlcl9uX3Nob3cgPSA4LCBzY2FsZV9mYWN0b3IgPSAxKSB7CiAgICAKICAgIGVucmljaG1lbnRfdGFibGUgPC0gCiAgICAgIGNsYXNzX3RhYmxlICU+JSAKICAgICAgc2VsZWN0KGdlbmUgPSBnZW5lX2NvbCwgCiAgICAgICAgICAgICBzcGVjID0gc3BlY19jb2wsIAogICAgICAgICAgICAgZW5yaWNoZWQgPSBlbnJpY2hlZF9jb2wpICU+JSAKICAgICAgZmlsdGVyKHNwZWMgJWluJSBzcGVjX2ZpbHRlcikgJT4lIAogICAgICBncm91cF9ieShlbnJpY2hlZCwgc3BlYykgJT4lIAogICAgICBzdW1tYXJpc2Uobl9nZW5lcyA9IG4oKSkgJT4lCiAgICAgIHVuZ3JvdXAoKQogICAgCiAgICBuZXRfZGF0YSA8LQogICAgICBlbnJpY2htZW50X3RhYmxlICU+JSAKICAgICAgbXV0YXRlKGFsbF9lbnJpY2hlZCA9IGVucmljaGVkKSAlPiUKICAgICAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZCwgc2VwID0gZW5yaWNoZWRfc2VwKSAlPiUKICAgICAgZ3JvdXBfYnkoZW5yaWNoZWQsIHNwZWMpICU+JSAKICAgICAgbXV0YXRlKHJhbmsgPSByYW5rKC1uX2dlbmVzLCB0aWVzLm1ldGhvZCA9ICJtaW4iKSkgJT4lCiAgICAgIGdyb3VwX2J5KGFsbF9lbnJpY2hlZCkgJT4lCiAgICAgIG11dGF0ZShhbnlfbG93X3JhbmsgPSBhbnkocmFuayA8PSBub2RlX2ZpbHRlcl9yYW5rKSkgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgCiAgICAgIGZpbHRlcigoc3BlYyA9PSBub2RlX2ZpbHRlcl9zaG93X2NhdCB8IGFueV9sb3dfcmFuayB8IG5fZ2VuZXMgPj0gbm9kZV9maWx0ZXJfbl9zaG93KSAmCiAgICAgICAgICAgICAgIChzcGVjID09IG5vZGVfZmlsdGVyX3Nob3dfY2F0IHwgbl9nZW5lcyA+PSBub2RlX2ZpbHRlcl9taW4pKSAlPiUgCiAgICAgIG11dGF0ZShlZGdlX2lkID0gcGFzdGUoImVucmljaGVkOiIsIGFsbF9lbnJpY2hlZCkpICU+JSAKICAgICAgYXJyYW5nZShuX2dlbmVzKQogICAgCiAgICBuZXRfZWRnZXMgPC0gCiAgICAgIG5ldF9kYXRhICUkJSAKICAgICAgdGliYmxlKG5vZGUxID0gZW5yaWNoZWQsIG5vZGUyID0gZWRnZV9pZCwgbiA9IG5fZ2VuZXMpICU+JSAKICAgICAgdW5pcXVlKCkKICAgIAogICAgZyA8LQogICAgICBuZXRfZWRnZXMgJT4lCiAgICAgIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkaXJlY3RlZCA9IEZBTFNFKSAlPiUKICAgICAgZ2dyYXBoKGxheW91dCA9ICJrayIpIAogICAgCiAgICBsaW5rX21hcCA8LSAKICAgICAgbmV0X2VkZ2VzICU+JSAKICAgICAgZ2F0aGVyKG5vZGUsIGlkLCAtKDMpKSAlPiUgCiAgICAgIG11dGF0ZSh0aXNzdWVfbm9kZSA9IG5vZGUgPT0gIm5vZGUxIiwgCiAgICAgICAgICAgICBjb2xvcl9pZCA9IGNhc2Vfd2hlbih0aXNzdWVfbm9kZSB+IGlkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCI7IiwgaWQpIH4gIkdyb3VwIGVucmljaGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFncmVwbCgiOyIsIGlkKSB+ICJUaXNzdWUgZW5yaWNoZWQiKSwgCiAgICAgICAgICAgICBsYWJlbCA9IGlmZWxzZSh0aXNzdWVfbm9kZSwgY29sb3JfaWQsIG4pKSAlPiUKICAgICAgc2VsZWN0KG4sIG5vZGUsIGlkLCB0aXNzdWVfbm9kZSwgY29sb3JfaWQsIGxhYmVsKSAlPiUKICAgICAgdW5pcXVlKCkKICAgIAogICAgCiAgICBlZGdlX2RhdGEgPC0gZ2V0X2VkZ2VzKCkoZyRkYXRhKQogICAgbm9kZV9kYXRhIDwtIAogICAgICBnZXRfbm9kZXMoKShnJGRhdGEpICU+JSAKICAgICAgYXNfdGliYmxlKCkgJT4lCiAgICAgIGxlZnRfam9pbihsaW5rX21hcCwgCiAgICAgICAgICAgICAgICBieSA9IGMoIm5hbWUiID0gImlkIikpIAogICAgCiAgICAKICAgIGZpZyA8LSAKICAgICAgZyArIAogICAgICBnZW9tX2VkZ2VfYXJjKGFlcyh3aWR0aCA9IG4pLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmF5IiwgCiAgICAgICAgICAgICAgICAgICAgc3RyZW5ndGggPSAwLCAKICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpICsgCiAgICAgIHNjYWxlX2VkZ2VfYWxwaGFfY29udGludW91cyhyYW5nZSA9IGMoMC4zLCAxKSkgKwogICAgICBzY2FsZV9lZGdlX3dpZHRoX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDMpKSArCiAgICAgIAogICAgICBnZW9tX25vZGVfcG9pbnQoZGF0YSA9IG5vZGVfZGF0YSAgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighdGlzc3VlX25vZGUpLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHNpemUgPSBsb2cxMChuKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNvbG9yX2lkKSwgCiAgICAgICAgICAgICAgICAgICAgICBzdHJva2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgIyBzaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDIxLAogICAgICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSsKICAgICAgZ2VvbV9ub2RlX3BvaW50KGRhdGEgPSBub2RlX2RhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih0aXNzdWVfbm9kZSksCiAgICAgICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IGNvbG9yX2lkKSwgCiAgICAgICAgICAgICAgICAgICAgICBzdHJva2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDIwICogc2NhbGVfZmFjdG9yLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwKICAgICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrCiAgICAgIGdlb21fbm9kZV90ZXh0KGRhdGEgPSBub2RlX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDQgKiBzY2FsZV9mYWN0b3IpICsKICAgICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYyg1LCAxMCkgKiBzY2FsZV9mYWN0b3IpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSArCiAgICAgIAogICAgICB0aGVtZV92b2lkKCkKICAgIAogICAgIyMgLS0tLS0gU2F2ZQogICAgCiAgICAKICAgIGN5dG9fc3VtbWFyeSA8LSAKICAgICAgbmV0X2VkZ2VzICU+JSAKICAgICAgbXV0YXRlKGNhdGVnb3J5ID0gaWZlbHNlKCFncmVwbChlbnJpY2hlZF9zZXAsIG5vZGUyKSwgIlRpc3N1ZSBlbnJpY2hlZCIsICJHcm91cCBlbnJpY2hlZCIpLCAKICAgICAgICAgICAgIG5vZGVfaWQgPSB1bmNsYXNzKGZhY3Rvcihub2RlMikpLAogICAgICAgICAgICAgbm9kZTEgPSBzdHJfdG9fc2VudGVuY2Uobm9kZTEpLCAKICAgICAgICAgICAgIG5fc3FydCA9IHNxcnQobiksIAogICAgICAgICAgICAgc3RyX2xlbiA9IHN0cl9sZW5ndGgobm9kZTEpKSAlPiUKICAgICAgc2VsZWN0KGNhdGVnb3J5LCBub2RlMSwgbm9kZTIsIG5vZGVfaWQsIG4sIG5fc3FydCwgc3RyX2xlbikgCiAgICAKICAgIGN5dG9fc3VtbWFyeSAlPiUgCiAgICAgIHdyaXRlX2RlbGltKCJjeXRvc2NhcGUgbm9kZXMgc3VtbWFyeS50eHQiLCBkZWxpbSA9ICJcdCIpCiAgIyAgICB3cml0ZV9kZWxpbShzYXZlcGF0aChwYXN0ZShzYXZlbmFtZSwgImN5dG9zY2FwZSBub2RlcyBzdW1tYXJ5LnR4dCIpKSwgZGVsaW0gPSAiXHQiKQogICAgCiAgICBiaW5kX3Jvd3MoY3l0b19zdW1tYXJ5ICU+JSAKICAgICAgICAgICAgICAgIGxlZnRfam9pbihwYWwgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5mcmFtZSgibm9kZTEiLCAiY29sb3IiKSkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KG5vZGVfaWQgPSBub2RlMSwgCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3IpICU+JSAKICAgICAgICAgICAgICAgIHVuaXF1ZSgpICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKG5vZGVfdHlwZSA9ICJUaXNzdWUiKSwKICAgICAgICAgICAgICBjeXRvX3N1bW1hcnkgJT4lIAogICAgICAgICAgICAgICAgbXV0YXRlKGNvbG9yID0gY2FzZV93aGVuKGNhdGVnb3J5ID09ICJUaXNzdWUgZW5yaWNoZWQiIH4gIiNlNDFhMWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3J5ID09ICJHcm91cCBlbnJpY2hlZCIgfiAiI0ZGOUQwMCIpLCAKICAgICAgICAgICAgICAgICAgICAgICBub2RlX2lkID0gYXMuY2hhcmFjdGVyKG5vZGVfaWQpKSAlPiUKICAgICAgICAgICAgICAgIHNlbGVjdChub2RlX2lkLCBjb2xvcikgJT4lIAogICAgICAgICAgICAgICAgdW5pcXVlKCkgJT4lCiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIG11dGF0ZShub2RlX3R5cGUgPSAiRW5yaWNobWVudCIpKSAlPiUKICAgICAgbXV0YXRlKGNvbG9yMiA9IGNhc2Vfd2hlbihub2RlX3R5cGUgPT0gIkVucmljaG1lbnQiIH4gY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV90eXBlID09ICJUaXNzdWUiIH4gIiNEM0QzRDNGRiIpLAogICAgICAgICAgICAgY29sb3IzID0gY2FzZV93aGVuKG5vZGVfdHlwZSA9PSAiRW5yaWNobWVudCIgfiBjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlX3R5cGUgPT0gIlRpc3N1ZSIgfiAiI0JFQkVCRUZGIikpICU+JSAKICAgICAgCiAgICAgIHdyaXRlX2RlbGltKCJjeXRvc2NhcGUgbm9kZXMgY29sb3IudHh0IiwgZGVsaW0gPSAiXHQiKSAKICAgICAgI3dyaXRlX2RlbGltKHNhdmVwYXRoKHBhc3RlKHNhdmVuYW1lLCAiY3l0b3NjYXBlIG5vZGVzIGNvbG9yLnR4dCIpKSwgZGVsaW0gPSAiXHQiKSAKICAgIAogICAgYmluZF9yb3dzKGN5dG9fc3VtbWFyeSAlPiUgCiAgICAgICAgICAgICAgICBzZWxlY3Qobm9kZV9pZCA9IG5vZGUxKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUobGFiZWwgPSBub2RlX2lkKSAlPiUKICAgICAgICAgICAgICAgIHVuaXF1ZSgpLAogICAgICAgICAgICAgIGN5dG9fc3VtbWFyeSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUobm9kZV9pZCA9IGFzLmNoYXJhY3Rlcihub2RlX2lkKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBhcy5jaGFyYWN0ZXIobikpICU+JQogICAgICAgICAgICAgICAgc2VsZWN0KG5vZGVfaWQsIGxhYmVsKSAlPiUgCiAgICAgICAgICAgICAgICB1bmlxdWUoKSkgJT4lIAogICAgICB3cml0ZV9kZWxpbSgiY3l0b3NjYXBlIG5vZGVzIGxhYmVsIHdob2xlIGdyb3VwLnR4dCIsCiAgICAgICN3cml0ZV9kZWxpbShzYXZlcGF0aChwYXN0ZShzYXZlbmFtZSwgImN5dG9zY2FwZSBub2RlcyBsYWJlbCB3aG9sZSBncm91cC50eHQiKSksIAogICAgICAgICAgICAgICAgICBkZWxpbSA9ICJcdCIpCiAgICAKICAgICMjIC0tLS0KICAgIAogICAgZmlnCiAgfQpgYGAKCmBgYHtyfQoKCm9yZ2FuX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KGNvbnNlbnN1c190aXNzdWVfbmFtZSwgb3JnYW5fY29sb3IpICU+JSB1bmlxdWUoKQpwYWwxIDwtICBvcmdhbl9jb2xvcnMkb3JnYW5fY29sb3IKcGFsMSA8LSBzZXROYW1lcyhwYWwxLCBvcmdhbl9jb2xvcnMkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKQoKc3BlY2lmaWNpdHlfcGFsZXR0ZSA8LSByZXYoYygiTm90IGRldGVjdGVkIiA9ICJncmV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIiA9ICJncmV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvdyB0aXNzdWUgc3BlY2lmaWNpdHkiID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5oYW5jZWQiID0gIiM5ODRlYTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAgZW5yaWNoZWQiID0gIiNGRjlEMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIGVucmljaGVkIiA9ICIjZTQxYTFjIikpCgpsaWJyYXJ5KGluZmx1ZW50aWFsKQoKY2xhc3NpZmljYXRpb25fbmV0d29ya19wbG90XzIoCiAgY2xhc3NfdGFibGUgPSBjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lIG11dGF0ZShzcGVjX2NhdGVnb3J5ID0gc3RyX3RvX3NlbnRlbmNlKHNwZWNfY2F0ZWdvcnkpKSwKICBnZW5lX2NvbCA9ICJnZW5lIiwKICBzcGVjX2NvbCA9ICJzcGVjX2NhdGVnb3J5IiwKICBlbnJpY2hlZF9jb2wgPSAiZW5yaWNoZWRfdGlzc3VlcyIsCiAgc3BlY19maWx0ZXIgPSBjKCJUaXNzdWUgZW5yaWNoZWQiLCAiR3JvdXAgZW5yaWNoZWQiKSwKICBwYWwgPSBjKHBhbDEsIHNwZWNpZmljaXR5X3BhbGV0dGUpLAogIHNhdmVuYW1lID0gInRlc3RfaW50ZXJjb25zZW5zdXMiLAogIGVucmljaGVkX3NlcCA9ICI7IiwKICBub2RlX2ZpbHRlcl9yYW5rID0gMiwKICBub2RlX2ZpbHRlcl9zaG93X2NhdCA9IGMoIlRpc3N1ZSBlbnJpY2hlZCIpLAogIG5vZGVfZmlsdGVyX21pbiA9IDIsCiAgbm9kZV9maWx0ZXJfbl9zaG93ID0gNSwKICBzY2FsZV9mYWN0b3IgPSAwLjgKKSAKCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvZGF0YV9wcmVzZW50YXRpb24vbmV3b3JrX3Bsb3QyLWZpbHRlci5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKYGBgCgojUmVhZCBDb21wYXJpc29uIGRhdGEKYGBge3J9CmNvbXBfbWV0YWRhdGEgPC0gcmVhZF9jc3YoIi4vZGF0YS9maW5hbF9kYXRhL2NvbXBhcmlzb25fbWV0YWRhdGEtaW5pdC0xLTAuY3N2IikKCmh1bWFuX2F0bGFzX3Rpc3N1ZSA8LQogIHJlYWRfZGVsaW0oIi4vZGF0YS9odW1hbl9ocGEvcm5hX3Rpc3N1ZV9jb25zZW5zdXMudHN2IiwgZGVsaW0gPSAiXHQiKSAlPiUKICBtdXRhdGUoc291cmNlID0gImhwYV9jb25zZW5zdXMiKSAlPiUKICBncm91cF9ieShHZW5lKSAlPiUKICBtdXRhdGUobnggPSBjYXNlX3doZW4oblRQTSA9PSAwIH4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgVCB+IG5UUE0gLyBzcXJ0KHNkKG5UUE0pKSkpICU+JQogIHVuZ3JvdXAoKSAjJT4lIAogICMgIyBqdXN0IG5lY2Vzc2FyeSBpZiByZWFkIGRhdGEgb2YgbXVsdGlwbGUgZGlmZmVyZW50IHNvdXJjZXMsIHRoZW4gd291bGQgbWFrZSBzZW5zZSB0byBhdmVyYWdlIG91dCBzYW1wbGVzIG5hbWVkIHRoZSBzYW1lCiAgIyAjIGp1c3QgZnJvbSBvbmUgc291cmNlIGF0bSwgc28gbm8gbmVlZCBmb3IgdGhpcy4KICAjIGdyb3VwX2J5KFRpc3N1ZSwgR2VuZSkgJT4lCiAgIyBzdW1tYXJpc2UoblRQTSA9IG1lYW4oblRQTSwgbmEucm0gPSBUKSkgJT4lIAogICMgdW5ncm91cCgpCgpocGFfY29tcCA8LSBodW1hbl9hdGxhc190aXNzdWUgJT4lIGxlZnRfam9pbigKICBjb21wX21ldGFkYXRhICU+JQogICAgc2VsZWN0KGhwYV9jb25zZW5zdXNfbmFtZSwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lCiAgICBkaXN0aW5jdCgpICU+JQogICAgZmlsdGVyKCFpcy5uYShocGFfY29uc2Vuc3VzX25hbWUpKSAlPiUKICAgIGZpbHRlcighaXMubmEoY29tcGFyaXNvbl90aXNzdWVfbmFtZSkpLAogIGJ5ID0gYygiVGlzc3VlIiA9ICJocGFfY29uc2Vuc3VzX25hbWUiKSkgJT4lIAogIGZpbHRlcighaXMubmEoY29tcGFyaXNvbl90aXNzdWVfbmFtZSkpICU+JSAKICBncm91cF9ieShHZW5lLGNvbXBhcmlzb25fdGlzc3VlX25hbWUpICU+JSAKICBzdW1tYXJpc2UodG1tID0gbWF4KG5UUE0pLAogICAgICAgICAgICBueCA9IG1heChueCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHJlbmFtZSh0aXNzdWUgPSBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKQoKcmF0X3Rpc3N1ZSA8LQogIHJlYWRfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9maW5hbF90bW1fdGlzc3VlX25hbWUuY3N2IikgJT4lIAogICMgI29taXQgZ2F0aGVyLCBkYXRhIHNob3VsZCBiZSAgYWxyZWFkeSBpbiBsb25nIGZvcm1hdAogICMgZ2F0aGVyKHNhbXBsZSwgdG1tLC0xKSAlPiUKICBncm91cF9ieSh0YXJnZXRfaWQpICU+JQogIG11dGF0ZShueCA9IGNhc2Vfd2hlbih0bW0gPT0gMCB+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIFQgfiB0bW0gLyBzcXJ0KHNkKHRtbSkpKSkgJT4lCiAgdW5ncm91cCgpCiAgCgojIHNlbGVjdCB0aXNzdWVzIGFuZCBwb29sIHRpc3N1ZXMgdGhhdCB3aWxsIGdldCBjb21wYXJlZApyYXRfdGlzc3VlX2NvbXAgPC0gcmF0X3Rpc3N1ZSAlPiUgbGVmdF9qb2luKGNvbXBfbWV0YWRhdGEgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHRpc3N1ZV9uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJ0aXNzdWVfbmFtZSIgPSAidGlzc3VlX25hbWUiKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSkgJT4lIAogIGdyb3VwX2J5KHRhcmdldF9pZCwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lIAogIHN1bW1hcmlzZSh0bW0gPSBtYXgodG1tKSwKICAgICAgICAgICAgbnggPSBtYXgobngpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICByZW5hbWUodGlzc3VlID0gY29tcGFyaXNvbl90aXNzdWVfbmFtZSkKCiNjaGVjayBpZiBhbGwgaGF2ZSB0aGUgc2FtZSB0aXNzdWUKYWxsKHJhdF90aXNzdWVfY29tcCAlPiUgZGlzdGluY3QodGlzc3VlKSA9PSBocGFfY29tcCAlPiUgZGlzdGluY3QodGlzc3VlKSApCgpgYGAKI1JlYWQgb3J0aG9sb2cgZGF0YQpgYGB7cn0KI1NlbGVjdCBlaXRoZXIgIkhQQSIgb3IgImVuc2VtYmxlIgpvcnRob2xvZ19kYXRhX2Zyb20gPC0gIkhQQSIKI29ydGhvbG9nX2RhdGFfZnJvbSA8LSAiZW5zZW1ibGUiCgppbmNsdWRlX21hbnkybWFueSA8LSBUUlVFCmluY2x1ZGVfb25lMm1hbnkgPC0gVFJVRQoKI09ubHkgbmVlZGVkIGZvciBlbnNlbWJsOgojZGVmaW5lIGVuc2VtYmxlIHZlcnNpb24KdmVyc2lvbiAgICAgPC0gMTAzCm9yZ2FuaXNtX2RiIDwtICJybm9ydmVnaWN1c19nZW5lX2Vuc2VtYmwiCiNtYXJ0IDwtIHVzZUVuc2VtYmwgKCBiaW9tYXJ0PSJnZW5lcyIgLCBkYXRhc2V0PW9yZ2FuaXNtX2RiICwgdmVyc2lvbj12ZXJzaW9uICkKCmlmIChvcnRob2xvZ19kYXRhX2Zyb20gPT0gIkhQQSIpewogIGhvbW9sb2dfZGF0YSA8LSByZWFkLnRhYmxlKCIuL2RhdGEvaHVtYW5faHBhL2thbGxlL2Vuc2VtYmxfcmF0X29ydGhvbG9nLnRzdiIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFRSVUUpICU+JSAKICAgIGZpbHRlcihlbnNybm9nX2lkICVpbiUgcmF0X3Rpc3N1ZSR0YXJnZXRfaWQpICU+JSAKICAgIGZpbHRlcihlbnNnX2lkICVpbiUgaHVtYW5fYXRsYXNfdGlzc3VlJEdlbmUpCn0gZWxzZSBpZiAob3J0aG9sb2dfZGF0YV9mcm9tID09ICJlbnNlbWJsZSIpewogIGhvbW9sb2dfZGF0YSA8LSBnZXRCTSggYXR0cmlidXRlcz1jKCAiZW5zZW1ibF9nZW5lX2lkIiwgImhzYXBpZW5zX2hvbW9sb2dfZW5zZW1ibF9nZW5lIiwgImhzYXBpZW5zX2hvbW9sb2dfb3J0aG9sb2d5X3R5cGUiKSAsIG1hcnQ9bWFydCApICU+JQogICAgZmlsdGVyKGhzYXBpZW5zX2hvbW9sb2dfZW5zZW1ibF9nZW5lICE9ICIiKSAlPiUgCiAgICBmaWx0ZXIoZW5zZW1ibF9nZW5lX2lkICVpbiUgcmF0X3Rpc3N1ZSR0YXJnZXRfaWQpICU+JSAKICAgIGZpbHRlcihoc2FwaWVuc19ob21vbG9nX2Vuc2VtYmxfZ2VuZSAlaW4lIGh1bWFuX2F0bGFzX3Rpc3N1ZSRHZW5lKSAlPiUgCiAgICByZW5hbWUoZW5zcm5vZ19pZCA9IGVuc2VtYmxfZ2VuZV9pZCwgZW5zZ19pZCA9IGhzYXBpZW5zX2hvbW9sb2dfZW5zZW1ibF9nZW5lLCBvcnRob2xvZ190eXBlID0gaHNhcGllbnNfaG9tb2xvZ19vcnRob2xvZ3lfdHlwZSkKfQoKaWYgKGluY2x1ZGVfbWFueTJtYW55ID09IEZBTFNFKXsKICBob21vbG9nX2RhdGEgPC0gaG9tb2xvZ19kYXRhICU+JSBmaWx0ZXIoIW9ydGhvbG9nX3R5cGUgPT0gIm9ydGhvbG9nX21hbnkybWFueSIpCn0KaWYgKGluY2x1ZGVfb25lMm1hbnkgPT0gRkFMU0UpewogIGhvbW9sb2dfZGF0YSA8LSBob21vbG9nX2RhdGEgJT4lIGZpbHRlcighb3J0aG9sb2dfdHlwZSA9PSAib3J0aG9sb2dfb25lMm1hbnkiKQp9CgpgYGAKCgojRmlndXJlIDYKYGBge3J9CmNvbXBhcmlzb25fY29sb3JzX3RibCA8LSByYmluZCgKICBjb21wX21ldGFkYXRhICU+JSBzZWxlY3QobmFtZSA9IGNvbXBhcmlzb25fdGlzc3VlX25hbWUsIGNvbG9yID0gY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcikgJT4lIGRpc3RpbmN0KCksIAogIGNvbXBfbWV0YWRhdGEgJT4lIHNlbGVjdChuYW1lID0gdGlzc3VlX25hbWUsIGNvbG9yID0gdGlzc3VlX2NvbG9yKSAlPiUgIGRpc3RpbmN0KCkKICApICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBkcm9wX25hKCkgJT4lIAogIG11dGF0ZShuYW1lID0gc3RyX3RvX3NlbnRlbmNlKG5hbWUpKQoKcGFsIDwtIGNvbXBhcmlzb25fY29sb3JzX3RibCRjb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBzdHJfdG9fc2VudGVuY2UoY29tcGFyaXNvbl9jb2xvcnNfdGJsJG5hbWUpKQoKCnBsb3RfZGF0YTEgPC0gCiAgY29tcF9tZXRhZGF0YSAlPiUgCiAgc2VsZWN0KHRpc3N1ZV9uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBvcmdhbl9uYW1lKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKHRpc3N1ZV9uYW1lID0gc3RyX3RvX3NlbnRlbmNlKHRpc3N1ZV9uYW1lKSwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSA9IHN0cl90b19zZW50ZW5jZShjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSkgJT4lIAogIGFycmFuZ2Uob3JnYW5fbmFtZSwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShwbG90X29yZGVyID0gcm93X251bWJlcigpKSAlPiUgc2VsZWN0KC1vcmdhbl9uYW1lKQoKcGxvdF9kYXRhMiA8LSAKICBwbG90X2RhdGExICU+JQogIGdhdGhlcihjb2x1bW4sIGxhYmVsLCAtcGxvdF9vcmRlcikgJT4lCiAgZ3JvdXBfYnkobGFiZWwsIGNvbHVtbikgJT4lIAogIHN1bW1hcmlzZShwbG90X29yZGVyID0gbWVhbihwbG90X29yZGVyKSkgICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGxhYmVsID0gbGFiZWwsCiAgICAgICAgIGNvbHVtbiA9IGZhY3Rvcihjb2x1bW4sCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ0aXNzdWVfbmFtZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tcGFyaXNvbl90aXNzdWVfbmFtZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpCnBsb3RfZGF0YTMgPC0gCiAgcGxvdF9kYXRhMSAlPiUgCiAgZ3JvdXBfYnkoY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShsZWZ0X3BvcyA9IG1lYW4ocGxvdF9vcmRlcikpCgpwbG90X2RhdGE0IDwtIAogIHBsb3RfZGF0YTIgJT4lIAogIGxlZnRfam9pbihjb21wYXJpc29uX2NvbG9yc190YmwsCiAgICAgICAgICAgIGJ5ID0gYygibGFiZWwiID0gIm5hbWUiKSkgJT4lIAogIGdyb3VwX2J5KGNvbHVtbiwgbGFiZWwpICU+JSAKICBzdW1tYXJpc2UobWlueSA9IG1pbihwbG90X29yZGVyKSAtIDAuNSwKICAgICAgICAgICAgbWF4eSA9IG1heChwbG90X29yZGVyKSArIDAuNSkKCmdncGxvdCgpICsKICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQsIAogICAgICAgICAgIGFlcyh4bWluID0gY29sdW1uLCB4bWF4ID0gY29sdW1uLCB5bWluID0gbWlueSwgeW1heCA9ICBtYXh5LCBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYKICAgICAgICAgKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQgJT4lIGZpbHRlciAoY29sdW1uID09ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIiksIAogICAgICAgICAgIyBhZXMoeG1pbiA9IGNvbHVtbiwgeG1heCA9IGNvbHVtbiwgeW1pbiA9IG1pbnksIHltYXggPSBtYXh5LCBjb2xvciA9IGNvbG9yKSwgCiAgICAgICAgICAgYWVzKHhtaW4gPSAyLCB4bWF4ID0gMi41LCB5bWluID0gbWlueSwgeW1heCA9ICBtYXh5LCBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYKICAgICAgICAgKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQgJT4lIGZpbHRlciAoY29sdW1uID09ICJ0aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMC41LCB4bWF4ID0gMSwgeW1pbiA9IG1pbnksIHltYXggPSAgbWF4eSwgZmlsbCA9IGxhYmVsKSwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCB3aWR0aCA9IDEwCiAgICAgICAgICkgKwogIGdlb21fc2VnbWVudCgKICAgIGRhdGEgPSBwbG90X2RhdGEzLAogICAgYWVzKAogICAgICB4ID0gImNvbXBhcmlzb25fdGlzc3VlX25hbWUiLAogICAgICB4ZW5kID0gInRpc3N1ZV9uYW1lIiwKICAgICAgeSA9IGxlZnRfcG9zLAogICAgICB5ZW5kID0gcGxvdF9vcmRlciwKICAgICAgY29sb3IgPSB0aXNzdWVfbmFtZQogICAgKSwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGFscGhhID0gMC41LAogICAgc2l6ZSA9IDIKICApKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBwbG90X2RhdGEyICU+JSBmaWx0ZXIoY29sdW1uID09ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIiksCiAgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgaGp1c3QgPSAwLAogICAgc2l6ZSA9IDIgKiA1IC8gNiwKICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAsICJtbSIpCiAgKSArCiAgZ2VvbV90ZXh0KAogICAgZGF0YSA9IHBsb3RfZGF0YTIgJT4lIGZpbHRlcihjb2x1bW4gPT0gInRpc3N1ZV9uYW1lIiksCiAgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgaGp1c3QgPSAxLAogICAgc2l6ZSA9IDIgKiA1IC8gNiwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAsICJtbSIpCiAgKSArCiAgIyBnZW9tX2xhYmVsKAogICMgICBkYXRhID0gcGxvdF9kYXRhMiAlPiUKICAjICAgICBmaWx0ZXIoY29sdW1uID09ICJvcmdhbl9uYW1lIiksCiAgIyAgICNhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICMgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gZ3N1YigiICIsICJcbiIsIGxhYmVsKSksCiAgIyAgIHNob3cubGVnZW5kID0gRiwKICAjICAgbGFiZWwuc2l6ZSA9IDAsCiAgIyAgIGhqdXN0ID0gMSwKICAjICAgbGluZWhlaWdodCA9IDAuNywKICAjICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIiksCiAgIyAgIHNpemUgPSAyICogNSAvIDYKICAjICkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyAgPSBjKCJSYXQgdGlzc3VlIiwgIkNvbXBhcmlzb24gdGlzc3VlIikscG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCkpCmdnc2F2ZSgiZmluYWxfcGxvdHMvY29tcGFyaXNvbi9jb21wYXJpc29uX3Rpc3N1ZXNfcmF0LnBkZiIsIGhlaWdodCA9IDUsIHdpZHRoID0gNCkKYGBgCmBgYHtyfQpjb21wYXJpc29uX2NvbG9yc190YmwgPC0gcmJpbmQoCiAgY29tcF9tZXRhZGF0YSAlPiUgc2VsZWN0KG5hbWUgPSBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBjb2xvciA9IGNvbnNlbnN1c190aXNzdWVfY29sb3IpICU+JSBkaXN0aW5jdCgpLCAKICBjb21wX21ldGFkYXRhICU+JSBzZWxlY3QobmFtZSA9IGhwYV9jb25zZW5zdXNfbmFtZSwgY29sb3IgPSB0aXNzdWVfY29sb3IpICU+JSAgZGlzdGluY3QoKQogICkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBzdHJfdG9fc2VudGVuY2UobmFtZSkpCgpwYWwgPC0gY29tcGFyaXNvbl9jb2xvcnNfdGJsJGNvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHN0cl90b19zZW50ZW5jZShjb21wYXJpc29uX2NvbG9yc190YmwkbmFtZSkpCgoKcGxvdF9kYXRhMSA8LSAKICBjb21wX21ldGFkYXRhICU+JSAKICBzZWxlY3QoaHBhX2NvbnNlbnN1c19uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBvcmdhbl9uYW1lKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKGhwYV9jb25zZW5zdXNfbmFtZSA9IHN0cl90b19zZW50ZW5jZShocGFfY29uc2Vuc3VzX25hbWUpLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lID0gc3RyX3RvX3NlbnRlbmNlKGNvbXBhcmlzb25fdGlzc3VlX25hbWUpKSAlPiUgCiAgYXJyYW5nZShvcmdhbl9uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSAlPiUgCiAgbXV0YXRlKHBsb3Rfb3JkZXIgPSByb3dfbnVtYmVyKCkpICU+JSBzZWxlY3QoLW9yZ2FuX25hbWUpCgpwbG90X2RhdGEyIDwtIAogIHBsb3RfZGF0YTEgJT4lCiAgZ2F0aGVyKGNvbHVtbiwgbGFiZWwsIC1wbG90X29yZGVyKSAlPiUKICBncm91cF9ieShsYWJlbCwgY29sdW1uKSAlPiUgCiAgc3VtbWFyaXNlKHBsb3Rfb3JkZXIgPSBtZWFuKHBsb3Rfb3JkZXIpKSAgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBsYWJlbCwKICAgICAgICAgY29sdW1uID0gZmFjdG9yKGNvbHVtbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGMoImNvbXBhcmlzb25fdGlzc3VlX25hbWUiLCAiaHBhX2NvbnNlbnN1c19uYW1lIgogICAgICAgICAgICAgICAgICAgICAgICAgICApKSkKcGxvdF9kYXRhMyA8LSAKICBwbG90X2RhdGExICU+JSAKICBncm91cF9ieShjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSAlPiUgCiAgbXV0YXRlKGxlZnRfcG9zID0gbWVhbihwbG90X29yZGVyKSkKCnBsb3RfZGF0YTQgPC0gCiAgcGxvdF9kYXRhMiAlPiUgCiAgbGVmdF9qb2luKGNvbXBhcmlzb25fY29sb3JzX3RibCwKICAgICAgICAgICAgYnkgPSBjKCJsYWJlbCIgPSAibmFtZSIpKSAlPiUgCiAgZ3JvdXBfYnkoY29sdW1uLCBsYWJlbCkgJT4lIAogIHN1bW1hcmlzZShtaW55ID0gbWluKHBsb3Rfb3JkZXIpIC0gMC41LAogICAgICAgICAgICBtYXh5ID0gbWF4KHBsb3Rfb3JkZXIpICsgMC41KQoKZ2dwbG90KCkgKwogIGdlb21fcmVjdChkYXRhID0gcGxvdF9kYXRhNCwgCiAgICAgICAgICAgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gIG1heHksIGZpbGwgPSBsYWJlbCksIAogICAgICAgICAgIHNob3cubGVnZW5kID0gRgogICAgICAgICApICsKICAgIGdlb21fcmVjdChkYXRhID0gcGxvdF9kYXRhNCAlPiUgZmlsdGVyIChjb2x1bW4gPT0gImhwYV9jb25zZW5zdXNfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMiwgeG1heCA9IDIuNSwgeW1pbiA9IG1pbnksIHltYXggPSAgbWF4eSwgZmlsbCA9IGxhYmVsKSwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGCiAgICAgICAgICkgKwogICAgZ2VvbV9yZWN0KGRhdGEgPSBwbG90X2RhdGE0ICU+JSBmaWx0ZXIgKGNvbHVtbiA9PSAiY29tcGFyaXNvbl90aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMC41LCB4bWF4ID0gMSwgeW1pbiA9IG1pbnksIHltYXggPSAgbWF4eSwgZmlsbCA9IGxhYmVsKSwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCB3aWR0aCA9IDEwCiAgICAgICAgICkgKwogIGdlb21fc2VnbWVudCgKICAgIGRhdGEgPSBwbG90X2RhdGEzLAogICAgYWVzKAogICAgICB4ID0gImNvbXBhcmlzb25fdGlzc3VlX25hbWUiLAogICAgICB4ZW5kID0gImhwYV9jb25zZW5zdXNfbmFtZSIsCiAgICAgIHkgPSBsZWZ0X3BvcywKICAgICAgeWVuZCA9IHBsb3Rfb3JkZXIsCiAgICAgIGNvbG9yID0gaHBhX2NvbnNlbnN1c19uYW1lCiAgICApLAogICAgc2hvdy5sZWdlbmQgPSBGLAogICAgYWxwaGEgPSAwLjUsCiAgICBzaXplID0gMgogICkrCiAgZ2VvbV90ZXh0KAogICAgZGF0YSA9IHBsb3RfZGF0YTIgJT4lIGZpbHRlcihjb2x1bW4gPT0gImhwYV9jb25zZW5zdXNfbmFtZSIpLAogICAgYWVzKHggPSBjb2x1bW4sIHkgPSBwbG90X29yZGVyLCBsYWJlbCA9IGxhYmVsKSwKICAgIGhqdXN0ID0gMCwKICAgIHNpemUgPSAyICogNSAvIDYsCiAgICBsYWJlbC5wYWRkaW5nID0gdW5pdCgwLCAibW0iKQogICkgKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBwbG90X2RhdGEyICU+JSBmaWx0ZXIoY29sdW1uID09ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIiksCiAgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgaGp1c3QgPSAxLAogICAgc2l6ZSA9IDIgKiA1IC8gNiwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAsICJtbSIpCiAgKSArCiAgIyBnZW9tX2xhYmVsKAogICMgICBkYXRhID0gcGxvdF9kYXRhMiAlPiUKICAjICAgICBmaWx0ZXIoY29sdW1uID09ICJvcmdhbl9uYW1lIiksCiAgIyAgICNhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICMgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gZ3N1YigiICIsICJcbiIsIGxhYmVsKSksCiAgIyAgIHNob3cubGVnZW5kID0gRiwKICAjICAgbGFiZWwuc2l6ZSA9IDAsCiAgIyAgIGhqdXN0ID0gMSwKICAjICAgbGluZWhlaWdodCA9IDAuNywKICAjICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIiksCiAgIyAgIHNpemUgPSAyICogNSAvIDYKICAjICkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyAgPSBjKCJDb21wYXJpc29uIHRpc3N1ZSIsICJIdW1hbiBUaXNzdWUiKSxwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoKSkKZ2dzYXZlKCJmaW5hbF9wbG90cy9jb21wYXJpc29uL2NvbXBhcmlzb25fdGlzc3Vlc19odW1hbl9yZXYucGRmIiwgaGVpZ2h0ID0gNSwgd2lkdGggPSA0KQoKYGBgCgoKI0JhdGNoIENvcnJlY3Rpb24KYGBge3J9CgojRG8gYmF0Y2ggY29ycmVjdGlvbgojam9pbiByYXQgYW5kIGh1bWFuIHRtbSBkYXRhIHRvZ2V0aGVyCmpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXAgPC0gcmF0X3Rpc3N1ZV9jb21wICU+JQogIGlubmVyX2pvaW4oaG9tb2xvZ19kYXRhLCBieSA9IGMoInRhcmdldF9pZCIgPSAiZW5zcm5vZ19pZCIpKSAlPiUKICB1bml0ZShtdXR1YWxfaWQsIHRhcmdldF9pZCwgZW5zZ19pZCwgc2VwID0gIl8iKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9ICJyYXQiKSAlPiUKICBzZWxlY3QobXV0dWFsX2lkLCB0aXNzdWUsIHNwZWNpZXMgLCB0bW0pICU+JQogIGJpbmRfcm93cygKICAgIGhwYV9jb21wICU+JQogICAgICBpbm5lcl9qb2luKGhvbW9sb2dfZGF0YSwgYnkgPSBjKCJHZW5lIiA9ICJlbnNnX2lkIikpICU+JQogICAgICB1bml0ZShtdXR1YWxfaWQsIGVuc3Jub2dfaWQsIEdlbmUsIHNlcCA9ICJfIikgJT4lCiAgICAgIG11dGF0ZShzcGVjaWVzID0gImh1bWFuIikgJT4lCiAgICAgIHNlbGVjdChtdXR1YWxfaWQsIHRpc3N1ZSwgc3BlY2llcyAsIHRtbSkKICApCiAgIyMjIyBpZiB0bW0gaXMgdW5kZXIgMCB0aGVuIGl0IGlzIGVxdWFsIHRvIDAKICAjbXV0YXRlKHRtbSA9IGlmZWxzZSh0bW0gPCAxLCAwLCB0bW0gKSkKCiNsb25nIHRhYmxlIHdpdGggaW5mIG9mIG9uIHRpc3N1ZSBhbmQgc3BlY2llcwpqb2luZWRfYXRsYXNfY29tcGFyaXNvbl90ZW1wXzIgPC0gam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcCAlPiUgCiAgdW5pdGUoaWQsIHRpc3N1ZSwgc3BlY2llcywgc2VwID0gIl8iKSAlPiUgCiAgc2VwYXJhdGUoaWQsIGludG8gPSBjKCJ0aXNzdWUiLCAic3BlY2llcyIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEYpIAoKI3dpZGUgdGFibGUgb25seSB3aXRoIHRtbQpqb2luZWRfYXRsYXNfY29tcGFyaXNvbl90ZW1wM190bW0gPC0KICBqb2luZWRfYXRsYXNfY29tcGFyaXNvbl90ZW1wXzIgJT4lIAogIHNlbGVjdChtdXR1YWxfaWQsIGlkLCB0bW0pICU+JSAKICBzcHJlYWQoaWQsIHRtbSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygibXV0dWFsX2lkIikgCgojbG9nIHNjYWxlCmpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXAzX2xpbW1hIDwtCiAgam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcDNfdG1tICU+JQogIHtsb2cxMCguICsgMSl9ICU+JQogIGxpbW1hOjpyZW1vdmVCYXRjaEVmZmVjdChiYXRjaCA9IGNvbG5hbWVzKGpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXAzX3RtbSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2V4dHJhY3QoIl8uKiQiKSkKI3B1dCB0aGVtIHRvZ2V0aGVyIGluIHRoZSBsb25nIGZvcm1hdApqb2luZWRfYXRsYXNfY29tcGFyaXNvbjwtIAogIGpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXBfMiAlPiUKICBsZWZ0X2pvaW4oam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcDNfdG1tICU+JSAKICAgICAgICAgICAgICBhc190aWJibGUocm93bmFtZXMgPSAibXV0dWFsX2lkIikgJT4lIAogICAgICAgICAgICAgIGdhdGhlcihpZCwgdG1tLCAtMSkpICU+JSAKICBsZWZ0X2pvaW4oam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcDNfbGltbWEgJT4lIAogICAgICAgICAgICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJtdXR1YWxfaWQiKSAlPiUgCiAgICAgICAgICAgICAgZ2F0aGVyKGlkLCBsaW1tYV9sb2cxcF90bW0sIC0xKSkgCmBgYAoKI0ZpZ3VyZSA3CiMjRmlndXJlIDdBIC0gQ3Jvc3Mgc3BlY2llcyBkZW5kcm9ncmFtbQpgYGB7cn0KCmhjbHVzdDRSTkFzZXEgPC0gZnVuY3Rpb24oZGYsIGNvcnJlbGF0aW9uX21ldGhvZCA9ICJzcGVhcm1hbiIpewogICN3aWRlIGRhdGFmcmFtZSBhcyBpbnB1dCAKICAjdG8gZ2V0IGNvcnJlbGF0aW9uIGJldHdlZW4gc2FtcGxlcywgd2hlcmUgcm93cyBhcmUgZ2VuZXMgY29sdW1ucyBhcmUgc2FtcGxlcwogICN0byBnZXQgY29ycmVsYXRpb24gYmV0d2VlbiBnZW5lcyBhY3Jvc3Mgc2FtcGxlcywgaW5wdXQgZGYgd2l0aCBnZW5lcyBhcyBjb2x1bW5zCiAgI2NhbiB1c2UgbGF0ZXIgZm9yIGRlbmRvZ3JhbSBtYWtpbmc6IGdnZGVuZHJvZ3JhbShbaGNsdXN0NFJOQXNlcV9yZXN1bHRzXSwgcm90YXRlID0gRkFMU0UsIHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIikKICBzaW1pbGFyaXR5IDwtIGNvcihkZiwgbWV0aG9kPWNvcnJlbGF0aW9uX21ldGhvZCwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQogIGRpc3NpbWlsYXJpdHkgPC0gMSAtIHNpbWlsYXJpdHkKICBoY2wgPC0gaGNsdXN0KGFzLmRpc3QoZGlzc2ltaWxhcml0eSksICJhdmVyYWdlIikKICByZXR1cm4gKGhjbCkKfSAKCgpvcmdhbl9jb2xvcnMgPC0gY29tcF9tZXRhZGF0YSAlPiUgc2VsZWN0KGNvbXBhcmlzb25fdGlzc3VlX25hbWUsIGNvbnNlbnN1c190aXNzdWVfY29sb3IpICU+JSBkcm9wX25hKCkgJT4lIGRpc3RpbmN0KCkKcGFsIDwtICBvcmdhbl9jb2xvcnMkY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBzdHJfdG9fc2VudGVuY2Uob3JnYW5fY29sb3JzJGNvbXBhcmlzb25fdGlzc3VlX25hbWUpKQoKc2hhcGVfZGVmIDwtIGMoMjEsIDIyKQpzaGFwZV9kZWYgPC0gc2V0TmFtZXMoc2hhcGVfZGVmLCBjKCJIdW1hbiIsICJSYXQiKSkKCgpwbG90X2NvbXBfZGVuZHJvX2RhdGEgPC0gam9pbmVkX2F0bGFzX2NvbXBhcmlzb24gJT4lIAogIHNlbGVjdChtdXR1YWxfaWQsIGlkLCBsaW1tYV9sb2cxcF90bW0pICU+JSAKICBzcHJlYWQoaWQsIGxpbW1hX2xvZzFwX3RtbSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygibXV0dWFsX2lkIikgJT4lIAogIGNvcihtZXRob2QgPSAic3BlYXJtYW4iLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogIHsxIC0gLn0gJT4lCiAgYXMuZGlzdCgpICU+JSAKICBoY2x1c3QobWV0aG9kID0gImF2ZXJhZ2UiKSAlVD4lCiAgcGxvdCAlPiUKICBkZW5kcm9fZGF0YSgpCgpkZW5kcm9fcGxvdF9kYXRhIDwtIAogIGxlZnRfam9pbihwbG90X2NvbXBfZGVuZHJvX2RhdGEkc2VnbWVudHMsIAogICAgICAgICAgICBwbG90X2NvbXBfZGVuZHJvX2RhdGEkbGFiZWxzLCAKICAgICAgICAgICAgYnkgPSBjKCJ4IiA9ICJ4IiwgInllbmQiID0gInkiKSkgJT4lIAogIHNlcGFyYXRlKGxhYmVsLCBjKCJ0aXNzdWUiLCAic3BlY2llcyIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgCiAgbXV0YXRlKHRpc3N1ZSA9IHN0cl90b19zZW50ZW5jZSh0aXNzdWUpLCBzcGVjaWVzID0gc3RyX3RvX3NlbnRlbmNlKHNwZWNpZXMpKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSBmYWN0b3Ioc3BlY2llcywgYygiSHVtYW4iLCAiUmF0IikpKQoKbGVmdF9wbG90IDwtIAogIGRlbmRyb19wbG90X2RhdGEgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChhZXMoeD15LCB5PXgsIHhlbmQ9eWVuZCwgeWVuZD14ZW5kKSkrCiAgIyBnZW9tX3JlY3QoYWVzKHhtaW49MCwgeW1pbj14ICsgMC41LCAKICAjICAgICAgICAgICAgICAgeG1heD0tMC4wMiwgeW1heD14ZW5kIC0gMC41LCAKICAjICAgICAgICAgICAgICAgZmlsbCA9IHRpc3N1ZSksIAogICMgICAgICAgICAgIHNob3cubGVnZW5kID0gRikgKwogIGdlb21fcG9pbnQoYWVzKAogICAgICB4ID0gMCwKICAgICAgeSA9IHgsCiAgICAgIHNoYXBlID0gc3BlY2llcywKICAgICAgZmlsbCA9IHRpc3N1ZQogICAgKSwKICAgIGNvbG9yID0gImdyYXkyNSIsCiAgICBhbHBoYSA9IDEsCiAgICBzaXplID0gMywKICAgIHN0cm9rZSA9IDAuNwogICkgKwogIGdlb21fdGV4dChhZXMoeD0tMC4wMiwgeT14LAogICAgICAgICAgICAgICAgbGFiZWwgPSB0aXNzdWUpLAogICAgICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZV9kZWYgKSArCiMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCwgZ3VpZGUgPSAibm9uZSIpICsKICAgc2NhbGVfeF9yZXZlcnNlKAogICAgIGV4cGFuZCA9IGV4cGFuc2lvbigwLjUgKSwgCiAgICAgcG9zaXRpb24gPSAidG9wIikrCiAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oMC4wMSkpICsKICB4bGFiKCIxIC0gU3BlYXJtYW4ncyByaG8iKSArCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwxLDEsMSksIHVuaXRzID0gIm1tIiksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpIApsZWZ0X3Bsb3QKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NvbXBhcmlzb24vY29tcGFyaXNvbl9kZW5kcm9ncmFtLnBkZiIsIHdpZHRoID0gNi41LCBoZWlnaHQgPSA5ICkKCmBgYAojI0ZpZ3VyZSA3QiAtIENyb3Mgc3BlY2llcyBVTUFQCmBgYHtyfQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShnZ3Bsb3RpZnkpCmxpYnJhcnkoZ2VvbXRleHRwYXRoKQoKY29tcF9tZXRhZGF0YSA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvY29tcGFyaXNvbl9tZXRhZGF0YS1pbml0LTEtMC5jc3YiKQp1bWFwX21ldGEgPC0gY29tcF9tZXRhZGF0YSAlPiUgc2VsZWN0KGNvbnNlbnN1c190aXNzdWVfY29sb3IsIG9yZ2FuX2NvbG9yLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSAlPiUgZmlsdGVyKGNvbXBhcmlzb25fdGlzc3VlX25hbWUgIT0gIiIgKSAlPiUgZGlzdGluY3QoKQp3aWRlX2RhdGEgPC0gam9pbmVkX2F0bGFzX2NvbXBhcmlzb24gJT4lIAogIHNlbGVjdCgtdGlzc3VlLCAtc3BlY2llcywgLXRtbSkgJT4lIAogIHNwcmVhZChpZCwgbGltbWFfbG9nMXBfdG1tKQpzZWVkIDwtIDQyCmZpbHRlcl96ZXJvX3NkID0gRgpuX2Vwb2NocyA9IDEwMDAgCm5fbmVpZ2hib3JzID0gMTUKCnBjYV9yZXMgPC0KICB3aWRlX2RhdGEgJT4lCiAgI25vIGxvZzFwIGJlY2F1c2UgbGltbWEgdmFsdWUgaXMgYWxyZWFkeSBjYWxjdWxhdGVkIGZyb20gbG9nIHNjYWxlIHZhbHVlCiAgY29sdW1uX3RvX3Jvd25hbWVzKGNvbG5hbWVzKHdpZGVfZGF0YSlbMV0pICU+JQogIHNjYWxlKCkgJT4lCiAgdCgpICU+JQogIHBjYShuUGNzID0gZGltKC4pWzFdKQoKcGNfbGltIDwtCiAgd2hpY2gocGNhX3Jlc0BSMmN1bSA+IDAuOClbMV0KCnBjX2xpbV9zZCA8LQogIHJldih3aGljaChwY2FfcmVzQHNEZXYgPiAxKSlbMV0KCm5fbmVpZ2hib3JzID0gMTUKc2V0LnNlZWQoc2VlZCkKdW1hcF9yZXMgPC0gcGNhX3Jlc0BzY29yZXNbLCAxOnBjX2xpbV0gJT4lCiAgdXdvdDo6dW1hcChuX25laWdoYm9ycyA9IG5fbmVpZ2hib3JzLAogICAgICAgICAgICAgbl9lcG9jaHMgPSBuX2Vwb2NocykgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgc2V0X25hbWVzKHBhc3RlMCgiVU1BUCIsIDE6bmNvbCguKSkpICU+JQogIG11dGF0ZShzYW1wbGUgPSByb3duYW1lcyhwY2FfcmVzQHNjb3JlcykpICU+JQogIHNlbGVjdChzYW1wbGUsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgc2VwYXJhdGUoCiAgICBzYW1wbGUsCiAgICBpbnRvID0gYygidGlzc3VlIiwgInNwZWNpZXMiKSwKICAgIHNlcCA9ICJfIiwKICAgIHJlbW92ZSA9IEYKICApICU+JQogIGxlZnRfam9pbih1bWFwX21ldGEsIGJ5ID0gYygidGlzc3VlIiA9ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIikpCgpvcmdhbl9jb2xvcnMgPC0KICB1bWFwX21ldGEgJT4lIHNlbGVjdChjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBjb25zZW5zdXNfdGlzc3VlX2NvbG9yKSAlPiUgdW5pcXVlKCkKcGFsIDwtICBvcmdhbl9jb2xvcnMkY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBvcmdhbl9jb2xvcnMkY29tcGFyaXNvbl90aXNzdWVfbmFtZSkKCnNoYXBlX2RlZiA8LSBjKDIxLCAyMikKc2hhcGVfZGVmIDwtIHNldE5hbWVzKHNoYXBlX2RlZiwgYygiaHVtYW4iLCAicmF0IikpCgoKIyB1bWFwX3JlcyAlPiUgIGdncGxvdChhZXMoVU1BUDEsIFVNQVAyLCAgY29sb3IgPSBvcmdhbl9uYW1lKSkgKwojICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkKcCA8LSB1bWFwX3JlcyAlPiUgIAogIGdncGxvdChhZXMoVU1BUDEsIFVNQVAyKSkgKwogIGdlb21fdGV4dHBhdGgoCiAgICBhZXMobGFiZWwgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlKSksCiAgICBoanVzdCA9IDAuNSwKICAgIHZqdXN0ID0gLTAuMywKICAgIGNvbG9yID0gImdyYXkyMCIKICApICsKICBnZW9tX3BvaW50KAogICAgYWVzKGZpbGwgPSB0aXNzdWUsIHNoYXBlID0gc3BlY2llcyksCiAgICBjb2xvciA9ICJncmF5MjUiLAogICAgYWxwaGEgPSAxLAogICAgc2l6ZSA9IDIsCiAgICBzdHJva2UgPSAwLjcKICApICsKICBndWlkZXMoZmlsbCA9ICJub25lIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZV9kZWYpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgICNsZWdlbmQudGl0bGUgPWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLTAuMSwgJ2NtJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiAgZ3VpZGVzKHNoYXBlID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBieXJvdyA9IFRSVUUsIHRpdGxlID0gIlNwZWNpZXMiKSkKCnAKCgoKCgppZiAoaW5jbHVkZV9tYW55Mm1hbnkgPT0gVFJVRSAmIGluY2x1ZGVfb25lMm1hbnkgPT0gVFJVRSkgewogIGNvbXBfdW1hcF9maWxlX25hbWUgPSBwYXN0ZTAoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3J0aG9sb2dfZGF0YV9mcm9tLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl91bWFwLnBkZiIpCn0gZWxzZSBpZiAoaW5jbHVkZV9tYW55Mm1hbnkgPT0gRkFMU0UgJiBpbmNsdWRlX29uZTJtYW55ID09IFRSVUUpIHsKICBjb21wX3VtYXBfZmlsZV9uYW1lID0gcGFzdGUwKCIuL2ZpbmFsX3Bsb3RzL2NvbXBhcmlzb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ydGhvbG9nX2RhdGFfZnJvbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfdW1hcC5wZGYiKQp9IGVsc2UgaWYgKGluY2x1ZGVfbWFueTJtYW55ID09IEZBTFNFICYKICAgICAgICAgICBpbmNsdWRlX29uZTJtYW55ID09IEZBTFNFKSB7CiAgY29tcF91bWFwX2ZpbGVfbmFtZSA9IHBhc3RlMCgiLi9maW5hbF9wbG90cy9jb21wYXJpc29uLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcnRob2xvZ19kYXRhX2Zyb20sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiX3VtYXAucGRmIikKfQoKCmdnc2F2ZShjb21wX3VtYXBfZmlsZV9uYW1lLCBoZWlnaHQgPSA1LjUsIHdpZHRoID0gNi41KQoKYGBgCgojI0ZpZ3VyZSA3QyAtIENyb3NzIHNwZWNpZXMgaHlwZXJnZW9tZXRyaWMgdGVzdApgYGB7cn0KI0Jhc2VkIG9uIE1heCBLYXJsc3NvbjogaHR0cHM6Ly9naXRodWIuY29tL21heGthcmxzc29uL1BpZy1BdGxhcy9ibG9iL21hc3Rlci9zY3JpcHRzL2Z1bmN0aW9uc19jbGFzc2lmaWNhdGlvbi5SCgpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dzY2kpCgoKCmNsYXNzMSA8LSBocGFfZ2VuZV9jbGFzc2lmaWNhdGlvbihkYXRhID0gcmF0X3Rpc3N1ZV9jb21wICU+JSBzZWxlY3QodGFyZ2V0X2lkLCB0aXNzdWUsIHRtbSksIGV4cHJlc3Npb25fY29sID0gInRtbSIsIHRpc3N1ZV9jb2wgPSAidGlzc3VlIiwgZ2VuZV9jb2wgPSAidGFyZ2V0X2lkIiwgZW5yX2ZvbGQgPSA0LCBtYXhfZ3JvdXBfbiA9IDUsIGRldF9saW0gPSAxKSAlPiUgcmVuYW1lKGVuc3Jub2dfaWQgPSBnZW5lKQpjbGFzczIgPC0gaHBhX2dlbmVfY2xhc3NpZmljYXRpb24oZGF0YSA9IGhwYV9jb21wICU+JSBzZWxlY3QoR2VuZSwgdGlzc3VlLCB0bW0pLCBleHByZXNzaW9uX2NvbCA9ICJ0bW0iLCB0aXNzdWVfY29sID0gInRpc3N1ZSIsIGdlbmVfY29sID0gIkdlbmUiLCBlbnJfZm9sZCA9IDQsIG1heF9ncm91cF9uID0gNSwgZGV0X2xpbSA9IDEpICU+JSAKICByZW5hbWUoZW5zZ19pZCA9IGdlbmUpCgpnZW5lX2NvbDEgPC0gImVuc3Jub2dfaWQiCmdlbmVfY29sMiA8LSAiZW5zZ19pZCIKc2VwIDwtICI7IgoKY2xhc3MxX2xvbmcgPC0KICBjbGFzczEgJT4lCiAgc2VsZWN0KGdlbmUxID0gZ2VuZV9jb2wxLCBlbnJpY2hlZF90aXNzdWVzKSAlPiUKICBtdXRhdGUoCiAgICBlbnJpY2hlZF90aXNzdWVzID0gaWZlbHNlKGlzLm5hKGVucmljaGVkX3Rpc3N1ZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibm90IGVucmljaGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5yaWNoZWRfdGlzc3VlcyksCiAgICBlbnJpY2hlZDEgPSBUCiAgKSAlPiUKICBzZXBhcmF0ZV9yb3dzKGVucmljaGVkX3Rpc3N1ZXMsIHNlcCA9IHNlcCkKCmNsYXNzMl9sb25nIDwtCiAgY2xhc3MyICU+JQogIHNlbGVjdChnZW5lMiA9IGdlbmVfY29sMiwgZW5yaWNoZWRfdGlzc3VlcykgJT4lCiAgbXV0YXRlKAogICAgZW5yaWNoZWRfdGlzc3VlcyA9IGlmZWxzZShpcy5uYShlbnJpY2hlZF90aXNzdWVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5vdCBlbnJpY2hlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaGVkX3Rpc3N1ZXMpLAogICAgZW5yaWNoZWQyID0gVAogICkgJT4lCiAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZF90aXNzdWVzLCBzZXAgPSBzZXApCgp0aXNfIDwtCiAgdW5pcXVlKGMoY2xhc3MxX2xvbmckZW5yaWNoZWRfdGlzc3VlcywKICAgICAgICAgICBjbGFzczJfbG9uZyRlbnJpY2hlZF90aXNzdWVzKSkgJT4lCiAgc29ydCgpCgpnZW5lX29ydGhvbG9ncyA8LSBob21vbG9nX2RhdGEgJT4lIHNlbGVjdCgxLDIpCgpvdmVybGFwX2h5cGVyX2FsbCA8LSBleHBhbmQuZ3JpZChpZDEgPSB0aXNfLAogICAgICAgICAgICBpZDIgPSB0aXNfLAogICAgICAgICAgICBnZW5lID0gMTpucm93KGdlbmVfb3J0aG9sb2dzKSkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgbGVmdF9qb2luKGdlbmVfb3J0aG9sb2dzICU+JQogICAgICAgICAgICAgIHNlbGVjdChnZW5lMSA9IGdlbmVfY29sMSwKICAgICAgICAgICAgICAgICAgICAgZ2VuZTIgPSBnZW5lX2NvbDIpICU+JQogICAgICAgICAgICAgIG11dGF0ZShnZW5lID0gcm93X251bWJlcigpKSkgJT4lCiAgc2VsZWN0KC1nZW5lKSAlPiUKICBsZWZ0X2pvaW4oY2xhc3MxX2xvbmcsCiAgICAgICAgICAgIGJ5ID0gYygiZ2VuZTEiLCAiaWQxIiA9ICJlbnJpY2hlZF90aXNzdWVzIikpICU+JQogIGxlZnRfam9pbihjbGFzczJfbG9uZywKICAgICAgICAgICAgYnkgPSBjKCJnZW5lMiIsICJpZDIiID0gImVucmljaGVkX3Rpc3N1ZXMiKSkgJT4lCiAgbXV0YXRlKAogICAgZW5yaWNoZWQxID0gaWZlbHNlKGlzLm5hKGVucmljaGVkMSksCiAgICAgICAgICAgICAgICAgICAgICAgRiwKICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2hlZDEpLAogICAgZW5yaWNoZWQyID0gaWZlbHNlKGlzLm5hKGVucmljaGVkMiksCiAgICAgICAgICAgICAgICAgICAgICAgRiwKICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2hlZDIpCiAgKSAlPiUKICBncm91cF9ieShpZDEsIGlkMiwgZW5yaWNoZWQxLCBlbnJpY2hlZDIpICU+JQogIGNvdW50KCkgJT4lCiAgZ3JvdXBfYnkoaWQxLCBpZDIpICU+JQogIHN1bW1hcmlzZSgKICAgICMgcSBpcyB0aGUgbnVtYmVyIG9mIHN1Y2Nlc3NlcwogICAgcSA9IHN1bShuW3doaWNoKGVucmljaGVkMSAmIGVucmljaGVkMildKSwKICAgIAogICAgIyBrIGlzIHRoZSBudW1iZXIgb2YgdHJpZXMgLSBpLmUuIHRoZSBudW1iZXIgb2YgZ2VuZXMgdGhhdCBhcmUgZWxldmF0ZWQgZm9yIGVpdGhlciBzcGVjaWVzCiAgICBrID0gc3VtKG5bd2hpY2goZW5yaWNoZWQxIHwgZW5yaWNoZWQyKV0pLAogICAgCiAgICAjIG0gaXMgdGhlIG51bWJlciBvZiBwb3NzaWJsZSBzdWNjZXNzZXMgLSBpLmUuIHRoZSBudW1iZXIgb2YgZ2VuZXMgdGhhdCBhcmUgZWxldmF0ZWQgZm9yIGVpdGhlcgogICAgbSA9IG1pbihzdW0oblt3aGljaChlbnJpY2hlZDEpXSksCiAgICAgICAgICAgIHN1bShuW3doaWNoKGVucmljaGVkMildKSksCiAgICAKICAgICMgbiBpcyB0aGUgcG9wdWxhdGlvbiBzaXplIC0gaS5lLiB0aGUgbnVtYmVyIG9mIGdlbmVzCiAgICBuID0gc3VtKG4pIC0gbQogICkgJT4lIAogIG11dGF0ZShwX3ZhbHVlID0gcGh5cGVyKHEgLSAxLCBtLCBuLCBrLCBsb3dlci50YWlsID0gRikpICU+JQogIG11dGF0ZShwX3ZhbHVlID0gaWZlbHNlKHBfdmFsdWUgPT0gMCwgLk1hY2hpbmUkZG91YmxlLnhtaW4sIHBfdmFsdWUpLAogICAgICAgICBhZGpfcHZhbCA9IHAuYWRqdXN0KHBfdmFsdWUsIG1ldGhvZCA9ICJCSCIpKSAlPiUKICByZW5hbWUocmF0X2lkID0gaWQxLCAKICAgICAgICAgaHVtYW5faWQgPSBpZDIpCgpvdmVybGFwX2h5cGVyX2FsbCAlPiUgCiAgd3JpdGVfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9yYXRfaHVtYW5fY2xhc3NfaHlwZXIuY3N2IikKCgpwbG90X29yZGVyIDwtIAogIG92ZXJsYXBfaHlwZXJfYWxsICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHJhdF9pZCA9IHN0cl90b19zZW50ZW5jZShyYXRfaWQpLAogICAgICAgICBodW1hbl9pZCA9IHN0cl90b19zZW50ZW5jZShodW1hbl9pZCkpICU+JQogIGZpbHRlcihyYXRfaWQgPT0gaHVtYW5faWQpICU+JQogIGFycmFuZ2UoYWRqX3B2YWwpICU+JQogIHB1bGwocmF0X2lkKQoKCnN0cmlwcGVkX3RoZW1lIDwtCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG91ciA9IE5BKSwKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9IE5BKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gTkEpLAogICAgICAgICNsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAjbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQua2V5LnNpemU9IHVuaXQoMC4zLCAiY20iKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91cj0iYmxhY2siLHNpemU9MC41KSkKCgpvdmVybGFwX2h5cGVyX2FsbCAlPiUgCiAgZ3JvdXBfYnkocmF0X2lkLCBodW1hbl9pZCkgJT4lCiAgbXV0YXRlKGNhcHBlZF9wID0gbWluKGMoLWxvZzEwKGFkal9wdmFsKSwgMjApKSkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUocmF0X2lkID0gc3RyX3RvX3NlbnRlbmNlKHJhdF9pZCksCiAgICAgICAgIGh1bWFuX2lkID0gc3RyX3RvX3NlbnRlbmNlKGh1bWFuX2lkKSkgJT4lCiAgbXV0YXRlKHJhdF9pZCA9IGZhY3RvcihyYXRfaWQsIHBsb3Rfb3JkZXIpLAogICAgICAgICBodW1hbl9pZCA9IGZhY3RvcihodW1hbl9pZCwgcGxvdF9vcmRlcikpICU+JQogIGdncGxvdChhZXMoaHVtYW5faWQsIHJhdF9pZCwgZmlsbCA9IGNhcHBlZF9wKSkgKwogIGdlb21fdGlsZSgpICsgCiAgZ2VvbV90aWxlKGRhdGEgPSAuICU+JSAKICAgICAgICAgICAgICBmaWx0ZXIoYWRqX3B2YWwgPj0gMC4wNSksCiAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAiRCIsIGRpcmVjdGlvbiA9IDEsIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkFkanVzdGVkIHAtdmFsdWUiKSArIAogIHN0cmlwcGVkX3RoZW1lICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC05MCwgaGp1c3QgPSAwLCB2anVzdCA9IDAuMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICB4bGFiKCJIdW1hbiIpICsgCiAgeWxhYigiUmF0IikKCiNnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi9PdmVybGFwIGh5cGVyIGhlYXRtYXAgZWxldmF0ZWQucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2KSAgCgpvdmVybGFwX2h5cGVyX2FsbCAlPiUgCiAgZmlsdGVyKGh1bWFuX2lkICE9ICJub3QgZW5yaWNoZWQiICYgCiAgICAgICAgICAgcmF0X2lkICE9ICJub3QgZW5yaWNoZWQiKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHJhdF9pZCA9IHN0cl90b19zZW50ZW5jZShyYXRfaWQpLAogICAgICAgICBodW1hbl9pZCA9IHN0cl90b19zZW50ZW5jZShodW1hbl9pZCkpICU+JQogIGZpbHRlcihhZGpfcHZhbCA8IDAuMDUpICU+JQogIG11dGF0ZShyYXRfaWQgPSBmYWN0b3IocmF0X2lkLCBwbG90X29yZGVyKSwKICAgICAgICAgaHVtYW5faWQgPSBmYWN0b3IoaHVtYW5faWQsIHBsb3Rfb3JkZXIpLAogICAgICAgICBhZGpfcHZhbCA9IGNhc2Vfd2hlbihhZGpfcHZhbCA8IDFlLTEwMCB+IDFlLTEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVCB+IGFkal9wdmFsKSkgJT4lCiAgZ2dwbG90KGFlcyhodW1hbl9pZCwgcmF0X2lkLCBmaWxsID0gLWxvZzEwKGFkal9wdmFsKSwgc2l6ZSA9IC1sb2cxMChhZGpfcHZhbCkpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCAKICAgICAgICAgICAgIGFscGhhID0gMC44LAogICAgICAgICAgICAgc3Ryb2tlID0gMC4yLAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArIAogICMgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAiRSIsIGRpcmVjdGlvbiA9IDEsIAogICMgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQWRqdXN0ZWQgcC12YWx1ZSIpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gZ2dzY2k6OnJnYl9tYXRlcmlhbChwYWxldHRlID0gImRlZXAtb3JhbmdlIikpICsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDYpKSArCiAgc3RyaXBwZWRfdGhlbWUgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gLTkwLCBoanVzdCA9IDAsIHZqdXN0ID0gMC4yKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiLCBzaXplID0gMC4yKSkgKwogIHhsYWIoIkh1bWFuIikgKyAKICB5bGFiKCJSYXQiKQoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NvbXBhcmlzb24vaHlwZXJnZW9tZXRyaWNfY29tcGFyaXNvbl90aXNzdWUucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1LjUpICAKCmBgYAoKIyNGaWd1cmUgN0QgLSBDcm9zcyBzcGVjaWVzIGdlbmUgYW5ub3RhdGlvbiBhbGx1dmlhbApgYGB7cn0KCiNjbGFzc2lmaWNhdGlvbiByYXQKY2xhc3NpZmljYXRpb25fcmF0X2NvbXAgPC0KICBocGFfZ2VuZV9jbGFzc2lmaWNhdGlvbigKICAgIGRhdGEgPSBqb2luZWRfYXRsYXNfY29tcGFyaXNvbiAlPiUgZmlsdGVyKHNwZWNpZXMgPT0gInJhdCIpICU+JSBzZWxlY3QoYygtaWQsLXNwZWNpZXMsLWxpbW1hX2xvZzFwX3RtbSkpLAogICAgZXhwcmVzc2lvbl9jb2wgPSAidG1tIiwKICAgIHRpc3N1ZV9jb2wgPSAidGlzc3VlIiwKICAgIGdlbmVfY29sID0gIm11dHVhbF9pZCIsCiAgICBlbnJfZm9sZCA9IDQsCiAgICBtYXhfZ3JvdXBfbiA9IDUsCiAgICBkZXRfbGltID0gMQogICkKCnJhdF9jb21wX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKAogIGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAicmF0IikgJT4lIHNlbGVjdCgtaWQsLXNwZWNpZXMsLWxpbW1hX2xvZzFwX3RtbSkgICU+JQogICAgc3ByZWFkKHRpc3N1ZSwgdG1tKSAlPiUKICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7CiAgICAgIGxvZzEwKHggKyAxKQogICAgfSkgJT4lCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoIm11dHVhbF9pZCIpCikKCmNsYXNzaWZpY2F0aW9uX3JhdF9jb21wIDwtIGNsYXNzaWZpY2F0aW9uX3JhdF9jb21wICU+JQogIGxlZnRfam9pbihyYXRfY29tcF90YXUsIGJ5ID0gYygiZ2VuZSIgPSAiZ2VuZSIpKSAlPiUKICBtdXRhdGUodGF1X3Njb3JlID0gaWZlbHNlKHNwZWNfY2F0ZWdvcnkgPT0gIm5vdCBkZXRlY3RlZCIsIE5BLCB0YXVfc2NvcmUpKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9ICJSYXQiKQoKCiNjbGFzc2lmaWNhdGlvbiBodW1hbgoKY2xhc3NpZmljYXRpb25faHVtYW5fY29tcCA8LQogIGhwYV9nZW5lX2NsYXNzaWZpY2F0aW9uKAogICAgZGF0YSA9IGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiaHVtYW4iKSAlPiUgc2VsZWN0KGMoLWlkLC1zcGVjaWVzLC1saW1tYV9sb2cxcF90bW0pKSwKICAgIGV4cHJlc3Npb25fY29sID0gInRtbSIsCiAgICB0aXNzdWVfY29sID0gInRpc3N1ZSIsCiAgICBnZW5lX2NvbCA9ICJtdXR1YWxfaWQiLAogICAgZW5yX2ZvbGQgPSA0LAogICAgbWF4X2dyb3VwX24gPSA1LAogICAgZGV0X2xpbSA9IDEKICApCgpodW1hbl9jb21wX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKAogIGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiaHVtYW4iKSAlPiUgc2VsZWN0KC1pZCwtc3BlY2llcywtbGltbWFfbG9nMXBfdG1tKSAgJT4lCiAgICBzcHJlYWQodGlzc3VlLCB0bW0pICU+JQogICAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHsKICAgICAgbG9nMTAoeCArIDEpCiAgICB9KSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygibXV0dWFsX2lkIikKKQoKY2xhc3NpZmljYXRpb25faHVtYW5fY29tcCA8LSBjbGFzc2lmaWNhdGlvbl9odW1hbl9jb21wICU+JQogIGxlZnRfam9pbihyYXRfY29tcF90YXUsIGJ5ID0gYygiZ2VuZSIgPSAiZ2VuZSIpKSAlPiUKICBtdXRhdGUodGF1X3Njb3JlID0gaWZlbHNlKHNwZWNfY2F0ZWdvcnkgPT0gIm5vdCBkZXRlY3RlZCIsIE5BLCB0YXVfc2NvcmUpKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9ICJIdW1hbiIpCgpgYGAKCgpgYGB7cn0KYWxsdXZpYWxfZGF0YV9jb21wIDwtIGNsYXNzaWZpY2F0aW9uX3JhdF9jb21wICU+JQogIHNlbGVjdChnZW5lLCBzcGVjX2NhdGVnb3J5KSAlPiUKICByZW5hbWUoUmF0ID0gc3BlY19jYXRlZ29yeSkgJT4lCiAgbGVmdF9qb2luKAogICAgY2xhc3NpZmljYXRpb25faHVtYW5fY29tcCAlPiUKICAgICAgc2VsZWN0KGdlbmUsIHNwZWNfY2F0ZWdvcnkpICU+JQogICAgICByZW5hbWUoSHVtYW4gPSBzcGVjX2NhdGVnb3J5KSwgCiAgICBieSA9ICJnZW5lIgogICkKCgp3aWR0aCA9IDAuMQoKYWxsdXZfMSA8LQogIAogIGFsbHV2aWFsX2RhdGFfY29tcCAlPiUKICBtdXRhdGUoUmF0ID0gc3RyX3RvX3NlbnRlbmNlKFJhdCksCiAgICAgICAgICBIdW1hbiA9IHN0cl90b19zZW50ZW5jZShIdW1hbikpICU+JQogIHNlbGVjdChSYXQsIAogICAgICAgICBIdW1hbikgJT4lCiAgbXV0YXRlKHJvd19uID0gcm93X251bWJlcigpKSAlPiUKICBnYXRoZXIoYmFyLCBjaHVuaywgLXJvd19uKSAlPiUKICBtdXRhdGUoY29sb3JfdmFycyA9IDEpICU+JQogIGdyb3VwX2J5KHJvd19uKSAlPiUKICBtdXRhdGUoY2h1bmtfY29sb3IgPSBjaHVua1ttYXRjaChjKCJIdW1hbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmF0IilbY29sb3JfdmFyc10sIGJhcildKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIAogIAogIG11dGF0ZShjaHVuayA9IGZhY3RvcihjaHVuaywgbGV2ZWxzID0gYygnVGlzc3VlIGVucmljaGVkJywgJ0dyb3VwIGVucmljaGVkJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdUaXNzdWUgZW5oYW5jZWQnLCAnTG93IHRpc3N1ZSBzcGVjaWZpY2l0eScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTm90IGRldGVjdGVkJykpLAogICAgICAgICBiYXIgPSBmYWN0b3IoYmFyLCBsZXZlbHMgPSBjKCJIdW1hbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJhdCIpKSkgJT4lCiAgCiAgCiAgZ2dwbG90KGFlcyh4ID0gYmFyLCBzdHJhdHVtID0gY2h1bmssIGFsbHV2aXVtID0gcm93X24sCiAgICAgICAgICAgICB5ID0gMSkpICsKICAKICBnZW9tX2Zsb3coYWVzKGZpbGwgPSBjaHVua19jb2xvciksIAogICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYsIHdpZHRoID0gd2lkdGgsCiAgICAgICAgICAgIGtub3QucG9zID0gMS82KSArCiAgZ2VvbV9zdHJhdHVtKGFlcyhmaWxsID0gY2h1bmspLCAKICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCBjb2xvciA9IE5BLCB3aWR0aCA9IHdpZHRoKSArCiAgCiAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBjKC4xLCAuMSksIHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKGdlbmVfY2F0ZWdvcnlfcGFsKSkgKyAKICAKICAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgY29vcmRfZmxpcCgpCgojIGFsbHV2XzEKCmZsb3dfZGF0YSA8LQogIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1sxXV0gJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgewogICAgaWYgKCJzaWRlIiAlaW4lIG5hbWVzKC4pKSB7CiAgICAgIC4KICAgIH0gZWxzZXsKICAgICAgbXV0YXRlKC4sCiAgICAgICAgICAgICBzaWRlID0gY2FzZV93aGVuKGZsb3cgPT0gImZyb20iIH4gInN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxvdyA9PSAidG8iIH4gImVuZCIpKQogICAgfQogIH0KCgoKc3RyYXR1bV9kYXRhIDwtIAogIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1syXV0KCmZsb3dfZGF0YV9sYWJlbHMgPC0KICBmbG93X2RhdGEgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgc2VsZWN0KHgsIHN0cmF0dW0sIGdyb3VwLCBzaWRlLCB5bWluLCB5bWF4KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2lkZSwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGMoeCwgc3RyYXR1bSwgeW1pbiwgeW1heCkpICU+JQogIG11dGF0ZV9hdCgKICAgIGMoCiAgICAgICJ4X2VuZCIsCiAgICAgICJ5bWF4X2VuZCIsCiAgICAgICJ5bWluX2VuZCIsCiAgICAgICJ4X3N0YXJ0IiwKICAgICAgInltYXhfc3RhcnQiLAogICAgICAieW1pbl9zdGFydCIKICAgICksCiAgICBhcy5udW1lcmljCiAgKSAlPiUKICBncm91cF9ieShzdHJhdHVtX3N0YXJ0LCBzdHJhdHVtX2VuZCwgeF9zdGFydCwgeF9lbmQpICU+JQogIHN1bW1hcmlzZSgKICAgIHlfZW5kID0gKG1pbih5bWluX2VuZCkgKyBtYXgoeW1heF9lbmQpKSAvIDIsCiAgICB5X3N0YXJ0ID0gKG1pbih5bWluX3N0YXJ0KSArIG1heCh5bWF4X3N0YXJ0KSkgLyAyLAogICAgc2l6ZSA9IG1heCh5bWF4X3N0YXJ0KSAtIG1pbih5bWluX3N0YXJ0KQogICkKCmFsbHV2XzIgPC0gCiAgYWxsdXZfMSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBmbG93X2RhdGFfbGFiZWxzLAogICAgICAgICAgICBhZXMoeCA9IHhfc3RhcnQgKyB3aWR0aC8yLAogICAgICAgICAgICAgICAgeSA9IHlfc3RhcnQsIAogICAgICAgICAgICAgICAgbGFiZWwgPSBzaXplKSwgCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRiwgCiAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICBhbmdsZSA9IC05MCwKICAgICAgICAgICAgaGp1c3QgPSAxLAogICAgICAgICAgICB2anVzdCA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmxvd19kYXRhX2xhYmVscywKICAgICAgICAgICAgYWVzKHggPSB4X2VuZCAtIHdpZHRoLzIsCiAgICAgICAgICAgICAgICB5ID0geV9lbmQsIAogICAgICAgICAgICAgICAgbGFiZWwgPSBzaXplKSwgCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRiwgCiAgICAgICAgICAgIHNpemUgPSAzLCAKICAgICAgICAgICAgYW5nbGUgPSAtOTAsCiAgICAgICAgICAgIGhqdXN0ID0gMCwKICAgICAgICAgICAgdmp1c3QgPSAwLjUpICsKICAKICAjIFN0cmF0dW0gbGFiZWwKICAKICBnZW9tX3RleHQoZGF0YSA9IHN0cmF0dW1fZGF0YSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeCA9PSAxKSwKICAgICAgICAgICAgYWVzKHggPSB4IC0gd2lkdGgvMiwKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgbGFiZWwgPSBzdHJhdHVtKSwKICAgICAgICAgICAgc2l6ZSA9IDQsIAogICAgICAgICAgICB2anVzdCA9IDEuNSwKICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGKSArIAogIGdlb21fdGV4dChkYXRhID0gc3RyYXR1bV9kYXRhICU+JQogICAgICAgICAgICAgIGZpbHRlcih4ID09IDIpLAogICAgICAgICAgICBhZXMoeCA9IHggKyB3aWR0aC8yLAogICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICBsYWJlbCA9IHN0cmF0dW0pLAogICAgICAgICAgICBzaXplID0gNCwgCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwKICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGKSArIAogIAogIGdlb21fdGV4dChkYXRhID0gc3RyYXR1bV9kYXRhLAogICAgICAgICAgICBhZXMoeCA9IHgsIAogICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICBsYWJlbCA9IHltYXggLSB5bWluKSwgCiAgICAgICAgICAgIHNpemUgPSA0LCAKICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwKICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGKQoKCmFsbHV2XzIKCmlmIChpbmNsdWRlX21hbnkybWFueSA9PSBUUlVFICYgaW5jbHVkZV9vbmUybWFueSA9PSBUUlVFKXsKICBjb21wX2FsbHV2X2ZpbGVfbmFtZSA9IHBhc3RlMCgiLi9maW5hbF9wbG90cy9jb21wYXJpc29uLyIsb3J0aG9sb2dfZGF0YV9mcm9tLCJfY29tcGFyaXNvbl9hbGxfYWxsdXZpYWwucGRmIikKfSBlbHNlIGlmIChpbmNsdWRlX21hbnkybWFueSA9PSBGQUxTRSAmIGluY2x1ZGVfb25lMm1hbnkgPT0gVFJVRSl7CiAgY29tcF9hbGx1dl9maWxlX25hbWUgPSBwYXN0ZTAoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi8iLG9ydGhvbG9nX2RhdGFfZnJvbSwiX2NvbXBhcmlzb25fb25seV9vbjJvbmUybWFueV9hbGx1dmlhbC5wZGYiKQp9IGVsc2UgaWYgKGluY2x1ZGVfbWFueTJtYW55ID09IEZBTFNFICYgaW5jbHVkZV9vbmUybWFueSA9PSBGQUxTRSl7CiAgY29tcF9hbGx1dl9maWxlX25hbWUgPSBwYXN0ZTAoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi8iLG9ydGhvbG9nX2RhdGFfZnJvbSwiX2NvbXBhcmlzb25fb25seV9vbmUyb25lX2FsbHV2aWFsLnBkZiIpfQoKZ2dzYXZlKGNvbXBfYWxsdXZfZmlsZV9uYW1lLHdpZHRoID0gOCwgaGVpZ2h0ID0gMykKCmBgYAoKI0ZpZ3VyZSBTMSAtIEJyYWluIE1ldGFkYXRhIGFsbHV2aWFsIHBsb3QKYGBge3J9CiNGdW5jdGlvbiBhZGFwdGVkIGZyb20gTWF4IEthcmxzc29uCm11bHRpX2FsbHV2aWFsX3Bsb3QgPC0KICBmdW5jdGlvbihkYXRhLAogICAgICAgICAgIHZhcnMsCiAgICAgICAgICAgY2h1bmtfbGV2ZWxzLAogICAgICAgICAgIHBhbCwKICAgICAgICAgICBjb2xvcl9ieSA9IGMoMSwgMywgMykpIHsKICAgIHNlbHZhcnMgPSB2YXJzCiAgICAKICAgIGlmICghaXMubnVsbChuYW1lcyh2YXJzKSkpIHsKICAgICAgdmFycyA9IG5hbWVzKHZhcnMpCiAgICB9CiAgICAKICAgIGFsbHV2XzEgPC0KICAgICAgZGF0YSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3Qoc2VsdmFycykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgbXV0YXRlKHJvd19uID0gcm93X251bWJlcigpKSAlPiUKICAgICAgZ2F0aGVyKGJhciwgY2h1bmssLXJvd19uKSAlPiUKICAgICAgbGVmdF9qb2luKHRpYmJsZShiYXIgPSB2YXJzLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX3ZhcnMgPSBjb2xvcl9ieSksCiAgICAgICAgICAgICAgICBieSA9ICJiYXIiKSAlPiUKICAgICAgZ3JvdXBfYnkocm93X24pICU+JQogICAgICBtdXRhdGUoY2h1bmtfY29sb3IgPSBjaHVua1ttYXRjaCh2YXJzW2NvbG9yX3ZhcnNdLCBiYXIpXSkgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgCiAgICAgIG11dGF0ZShjaHVuayA9IGZhY3RvcihjaHVuaywgbGV2ZWxzID0gY2h1bmtfbGV2ZWxzKSwKICAgICAgICAgICAgIGJhciA9IGZhY3RvcihiYXIsIGxldmVscyA9IHZhcnMpKSAlPiUKICAgICAgCiAgICAgIAogICAgICBnZ3Bsb3QoYWVzKAogICAgICAgIHggPSBiYXIsCiAgICAgICAgc3RyYXR1bSA9IGNodW5rLAogICAgICAgIGFsbHV2aXVtID0gcm93X24sCiAgICAgICAgeSA9IDEKICAgICAgKSkgKwogICAgICAKICAgICAgZ2VvbV9mbG93KGFlcyhmaWxsID0gY2h1bmtfY29sb3IpLAogICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArCiAgICAgIGdlb21fc3RyYXR1bShhZXMoZmlsbCA9IGNodW5rKSwKICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRiwgY29sb3IgPSBOQSkgKwogICAgICAKICAgICAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBjKC4xLCAuMSksIHBvc2l0aW9uID0gInRvcCIpICsKICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwoKCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgKQoKCiAgICAKICAgIAogICAgZmxvd19kYXRhIDwtCiAgICAgIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1sxXV0gJT4lCiAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICB7CiAgICAgICAgaWYgKCJzaWRlIiAlaW4lIG5hbWVzKC4pKSB7CiAgICAgICAgICAuCiAgICAgICAgfSBlbHNlewogICAgICAgICAgbXV0YXRlKC4sCiAgICAgICAgICAgICAgICAgc2lkZSA9IGNhc2Vfd2hlbihmbG93ID09ICJmcm9tIiB+ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbG93ID09ICJ0byIgfiAiZW5kIikpCiAgICAgICAgfQogICAgICB9CiAgICAKICAgIAogICAgc3RyYXR1bV9kYXRhIDwtCiAgICAgIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1syXV0KICAgIAogICAgZmxvd19kYXRhX2xhYmVscyA8LQogICAgICBmbG93X2RhdGEgJT4lCiAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICAKICAgICAgc2VsZWN0KHgsIHN0cmF0dW0sIGdyb3VwLCBzaWRlLCB5bWluLCB5bWF4KSAlPiUKICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNpZGUsCiAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYyh4LCBzdHJhdHVtLCB5bWluLCB5bWF4KSkgJT4lCiAgICAgIAogICAgICBtdXRhdGVfYXQoCiAgICAgICAgYygKICAgICAgICAgICJ4X2VuZCIsCiAgICAgICAgICAieW1heF9lbmQiLAogICAgICAgICAgInltaW5fZW5kIiwKICAgICAgICAgICJ4X3N0YXJ0IiwKICAgICAgICAgICJ5bWF4X3N0YXJ0IiwKICAgICAgICAgICJ5bWluX3N0YXJ0IgogICAgICAgICksCiAgICAgICAgYXMubnVtZXJpYwogICAgICApICU+JQogICAgICBncm91cF9ieShzdHJhdHVtX3N0YXJ0LCBzdHJhdHVtX2VuZCwgeF9zdGFydCwgeF9lbmQpICU+JQogICAgICBzdW1tYXJpc2UoCiAgICAgICAgeV9lbmQgPSAobWluKHltaW5fZW5kKSArIG1heCh5bWF4X2VuZCkpIC8gMiwKICAgICAgICB5X3N0YXJ0ID0gKG1pbih5bWluX3N0YXJ0KSArIG1heCh5bWF4X3N0YXJ0KSkgLyAyLAogICAgICAgIHNpemUgPSBtYXgoeW1heF9zdGFydCkgLSBtaW4oeW1pbl9zdGFydCkKICAgICAgKQogICAgCiAgICBhbGx1dl8xIDwtCiAgICAgIGFsbHV2XzEgKwogICAgICAjIGdlb21fdGV4dCgKICAgICAgIyAgIGRhdGEgPSBmbG93X2RhdGFfbGFiZWxzLAogICAgICAjICAgYWVzKHggPSB4X3N0YXJ0ICsgMSAvIDYsCiAgICAgICMgICAgICAgeSA9IHlfc3RhcnQsCiAgICAgICMgICAgICAgbGFiZWwgPSBzaXplKSwKICAgICAgIyAgIGluaGVyaXQuYWVzID0gRiwKICAgICAgIyAgIHNpemUgPSAzLAogICAgICAjICAgaGp1c3QgPSAwCiAgICAgICMgKSArCiAgICAgICMgZ2VvbV90ZXh0KAogICAgICAjICAgZGF0YSA9IGZsb3dfZGF0YV9sYWJlbHMsCiAgICAgICMgICBhZXMoeCA9IHhfZW5kIC0gMSAvIDYsCiAgICAgICMgICAgICAgeSA9IHlfZW5kLAogICAgICAjICAgICAgIGxhYmVsID0gc2l6ZSksCiAgICAgICMgICBpbmhlcml0LmFlcyA9IEYsCiAgICAgICMgICBzaXplID0gMywKICAgICAgIyAgIGhqdXN0ID0gMQogICAgICAjICkgKwogICAgICAjIAogICAgICAjIFN0cmF0dW0gbGFiZWwKICAgICAgZ2VvbV90ZXh0KAogICAgICAgIGRhdGEgPSBzdHJhdHVtX2RhdGEsCiAgICAgICAgYWVzKAogICAgICAgICAgeCA9IHgsCiAgICAgICAgICB5ID0geSwKICAgICAgICAgIGxhYmVsID0gcGFzdGUoc3RyYXR1bSMsIAogICAgICAgICAgICAgICAgICAgICAgICMgcGFzdGUoIlsiLCB5bWF4IC0geW1pbiwgIl0iLCBzZXAgPSAiIikKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICksCiAgICAgICAgc2l6ZSA9IDQsCiAgICAgICAgaW5oZXJpdC5hZXMgPSBGCiAgICAgICkKICAgICAgICAgICAgICAgIAogICAgCiAgICBhbGx1dl8xCiAgfQpgYGAKCmBgYHtyfQptZXRhZGF0YV9iIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIob3JnYW5fbmFtZSA9PSJCcmFpbiIpCgp0X25hbWVzIDwtIG1ldGFkYXRhX2IkdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCnJfbmFtZXMgPC0gbWV0YWRhdGFfYiRyZWdpb25fdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCmNfbmFtZXMgPC0gbWV0YWRhdGFfYiRjb25zZW5zdXNfdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCm9fbmFtZXMgPC0gbWV0YWRhdGFfYiRvcmdhbl9uYW1lICU+JSB1bmlxdWUoKQoKdF9jb2xvcnMgPC0gbWV0YWRhdGFfYiAlPiUgc2VsZWN0KHRpc3N1ZV9uYW1lLCB0aXNzdWVfY29sb3IpICU+JSB1bmlxdWUoKSAlPiUgcmVuYW1lIChjaHVuayA9IHRpc3N1ZV9uYW1lLCBjb2xvciA9IHRpc3N1ZV9jb2xvcikKcl9jb2xvcnMgPC0gbWV0YWRhdGFfYiAlPiUgc2VsZWN0KHJlZ2lvbl90aXNzdWVfbmFtZSwgcmVnaW9uX3Rpc3N1ZV9jb2xvcikgJT4lIHVuaXF1ZSgpICU+JSByZW5hbWUgKGNodW5rID0gcmVnaW9uX3Rpc3N1ZV9uYW1lLCBjb2xvciA9IHJlZ2lvbl90aXNzdWVfY29sb3IpCmNfY29sb3JzIDwtIG1ldGFkYXRhX2IgJT4lIHNlbGVjdChjb25zZW5zdXNfdGlzc3VlX25hbWUsIGNvbnNlbnN1c190aXNzdWVfY29sb3IpICU+JSB1bmlxdWUoKSAlPiUgcmVuYW1lIChjaHVuayA9IGNvbnNlbnN1c190aXNzdWVfbmFtZSwgY29sb3IgPSBjb25zZW5zdXNfdGlzc3VlX2NvbG9yKQpvX2NvbG9ycyA8LSBtZXRhZGF0YV9iICU+JSBzZWxlY3Qob3JnYW5fbmFtZSwgb3JnYW5fY29sb3IpICU+JSB1bmlxdWUoKSAlPiUgcmVuYW1lIChjaHVuayA9IG9yZ2FuX25hbWUsIGNvbG9yID0gb3JnYW5fY29sb3IpCgpiaW5kX2NvbG9ycyA8LSBiaW5kX3Jvd3ModF9jb2xvcnMsIHJfY29sb3JzLCBvX2NvbG9ycykgJT4lIHVuaXF1ZSgpICU+JSBhcnJhbmdlKGNodW5rKSAlPiUgbXV0YXRlKHJvd19uID0gcm93X251bWJlcigpKQpwYWwgPC0gIGJpbmRfY29sb3JzJGNvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIGJpbmRfY29sb3JzJGNodW5rKQoKZGF0YSA9IG1ldGFkYXRhX2IKdmFycyA9IGMoInRpc3N1ZV9uYW1lIiwgInJlZ2lvbl90aXNzdWVfbmFtZSIsICJvcmdhbl9uYW1lIikKY2h1bmtfbGV2ZWxzID0gYyh0X25hbWVzLCByX25hbWVzLCBvX25hbWVzKSAlPiUgdW5pcXVlKCkKY29sb3JfYnkgPSBjKDEsIDMsIDMpCgptdWx0aV9hbGx1dmlhbF9wbG90KGRhdGEgPSBtZXRhZGF0YV9iLCB2YXJzID0gdmFycywgY2h1bmtfbGV2ZWxzID0gY2h1bmtfbGV2ZWxzLCBwYWwgPSBwYWwsIGNvbG9yX2J5ID0gYygxLCAzLCAzKSkKCmdnc2F2ZSgiZmluYWxfcGxvdHMvYWxsdXZpYWwvYnJhaW4tdGNvLTFfcC5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDEwKQpgYGAKCgojRmlndXJlIFMyIC0gTm9ybWFsaXNhdGlvbiBjb21wYXJpc29uIFRQTSB2cyBuVFBNIChUTU0pCmBgYHtyfQp0cG1fc2FtcGxlIDwtcmVhZF9jc3YoIi4vZGF0YS9maW5hbF9kYXRhL2N1cmF0ZWRfcFRQTV9yYXR0dXNfbm9ydmVnaWN1c192MTAzLmNzdiIpCgoKc2FtcGxlX3N1YnNldF9JRCA8LSBtZXRhZGF0YVttYXRjaCh1bmlxdWUobWV0YWRhdGEkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSwgbWV0YWRhdGEkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSxdICU+JSBzZWxlY3QoSUQpCnNhbXBsZV9zdWJzZXRfdG1tIDwtIHRtbV9zYW1wbGUgJT4lIHNlbGVjdCh0YXJnZXRfaWQsIHNhbXBsZV9zdWJzZXRfSUQkSUQpICU+JSBnYXRoZXIoc2FtcGxlLCB0bW0sIC0xKQpzYW1wbGVfc3Vic2V0X3RwbSA8LSB0cG1fc2FtcGxlICU+JSBzZWxlY3QodGFyZ2V0X2lkLCBzYW1wbGVfc3Vic2V0X0lEJElEKSAlPiUgZ2F0aGVyKHNhbXBsZSwgdHBtLCAtMSkKCiMgcGxvdF9kYXRhIDwtIGxlZnRfam9pbihzYW1wbGVfc3Vic2V0X3RtbSwgc2FtcGxlX3N1YnNldF90cG0sIGJ5ID0gYygidGFyZ2V0X2lkIiwgInNhbXBsZSIpKSAlPiUKIyAgIG11dGF0ZShsb2cxcF90bW0gPSBsb2cxMCh0bW0gKyAxKSwgbG9nMXBfdHBtID0gbG9nMTAodHBtICsxKSkgJT4lIHNlbGVjdCgtdG1tLCAtdHBtKSAlPiUgCiMgICBnYXRoZXIoZXhwcmVzc2lvbl90eXBlLCBleHByZXNzaW9uLCAtMSwgLTIpICU+JSAKIyAgIGxlZnRfam9pbihtZXRhZGF0YSAlPiUgc2VsZWN0KElELCB0aXNzdWVfbmFtZSwgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJzYW1wbGUiID0gIklEIikpCgpwbG90X2RhdGEgPC0gbGVmdF9qb2luKHNhbXBsZV9zdWJzZXRfdG1tLCBzYW1wbGVfc3Vic2V0X3RwbSwgYnkgPSBjKCJ0YXJnZXRfaWQiLCAic2FtcGxlIikpICU+JQogIGdhdGhlcihleHByZXNzaW9uX3R5cGUsIGV4cHJlc3Npb24sIC0xLCAtMikgJT4lIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICBsb2cxMCh4ICsgMSkKICAgICAgICAgICAgICB9KSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhICU+JSBzZWxlY3QoSUQsIHRpc3N1ZV9uYW1lLCBjb25zZW5zdXNfdGlzc3VlX25hbWUpLCBieSA9IGMoInNhbXBsZSIgPSAiSUQiKSkKCgpwYWxfdGJsIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QoY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lLCBjb25zZW5zdXNfdGlzc3VlX2NvbG9yKSAlPiUgZGlzdGluY3QoKQpwYWwgPC0gcGFsX3RibCAlPiUgcHVsbChjb25zZW5zdXNfdGlzc3VlX2NvbG9yKQpwYWwgPC0gc2V0TmFtZXMocGFsLCBwYWxfdGJsICU+JSBwdWxsKGNvbnNlbnN1c190aXNzdWVfbmFtZSkpCgoKZ2dwbG90KGRhdGEgPSBwbG90X2RhdGEsIGFlcyh4ID0gZXhwcmVzc2lvbiwgeSA9c2FtcGxlLCBmaWxsID0gY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSkgKwogIGdlb21fYm94cGxvdChkcmF3X3F1YW50aWxlcyA9IDAuNSwgb3V0bGllci5zaXplID0gMC41LCBvdXRsaWVyLmFscGhhID0gMC4zKSsKICBmYWNldF93cmFwKH5leHByZXNzaW9uX3R5cGUpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy90cG1fdG1tX2NvbXBfYm94cGxvdC5wZGYiLCB3aWR0aD03LCBoZWlnaHQgPSAxMikKCmBgYAoKI0ZpZ3VyZSBTMyAtIFNwZWFybWFuIGhlYXRtYXAgKHRpc3N5ZSB0eXBlIGxldmVsKQpgYGB7cn0KCiMjU3BlYXJtYW4ncyByb2ggaGVhdG1hcCBhdCB0aXNzdWUgdHlwZSBsZXZlbAppZihmaWxlLmV4aXN0cygiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpKSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpCn0gZWxzZSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSAgdG1tX3Rpc3N1ZSAlPiUKICAgIHNwcmVhZCh0aXNzdWVfbmFtZSwgdG1tKSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIikKICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZSh0aXNzdWVfdG1tX3NwZWFybWFuKSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIiksIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfdGlzc3Vlcy5jc3YiKQp9Cgp0aXNzdWVfdG1tX3NwZWFybWFuICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoInRpc3N1ZV9uYW1lIikgJT4lIAogIHBoZWF0bWFwKAogICAjIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLAogICAgY2VsbGhlaWdodCA9IDgsCiAgICBjZWxsd2lkdGggPSA4LCAKICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgY29sb3IgPSB2aXJpZGlzOjppbmZlcm5vKDIwLCBkaXJlY3Rpb24gPSAtMSksCiAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIAogICAgKSAlPiUgCiAgYXMuZ2dwbG90KCkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9zcGVhcm1hbl9jb3JyX3Rpc3N1ZS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKCgpgYGAKCiNGaWd1cmUgUzQgLSBDb21wYXJpc29uIFBoZWF0bWFwCmBgYHtyfQppZihmaWxlLmV4aXN0cygiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpKSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpCn0gZWxzZSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSAgdG1tX3Rpc3N1ZSAlPiUKICAgIHNwcmVhZCh0aXNzdWVfbmFtZSwgdG1tKSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIikKICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZSh0aXNzdWVfdG1tX3NwZWFybWFuKSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIiksIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfdGlzc3Vlcy5jc3YiKQp9CgoKCmpvaW5lZF9hdGxhc19jb21wYXJpc29uX3BoZWF0X2RhdGEgPC0gIGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JQogIHNlbGVjdChtdXR1YWxfaWQsIGlkLGxpbW1hX2xvZzFwX3RtbSApICU+JSAgCiAgc3ByZWFkKGlkLCBsaW1tYV9sb2cxcF90bW0pICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoIm11dHVhbF9pZCIpICU+JSAKICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAidGlzc3VlX25hbWUiKQoKam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fcGhlYXRfZGF0YSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0aXNzdWVfbmFtZSIpICU+JSAKICBwaGVhdG1hcCgKICAgIyBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwKICAgIGNlbGxoZWlnaHQgPSA4LAogICAgY2VsbHdpZHRoID0gOCwgCiAgICBib3JkZXJfY29sb3IgPSBOQSwKICAgIGNvbG9yID0gdmlyaWRpczo6aW5mZXJubygyMCwgZGlyZWN0aW9uID0gLTEpLAogICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCAKICAgICkgJT4lIAogIGFzLmdncGxvdCgpCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi9waGVhdG1hcC5wZGYiLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAyMCkKCmBgYApgYGB7cn0Kc2Vzc2lvbkluZm8oKQoKYGBgCgo=